Fix destroyed key clearing.

This commit is contained in:
John Preston 2019-11-20 16:33:45 +03:00
parent 4c24ec7725
commit 4edfd4804f
11 changed files with 108 additions and 147 deletions

View File

@ -378,13 +378,12 @@ void Account::startMtp() {
void Account::logOut() {
if (_loggingOut) {
return;
}
_loggingOut = true;
if (_mtp) {
_mtp->logout(::rpcDone([=] {
loggedOut();
}), ::rpcFail([=] {
loggedOut();
return true;
}));
_mtp->logout([=] { loggedOut(); });
} else {
// We log out because we've forgotten passcode.
loggedOut();
@ -399,6 +398,7 @@ void Account::forcedLogOut() {
}
void Account::loggedOut() {
_loggingOut = false;
if (Global::LocalPasscode()) {
Global::SetLocalPasscode(false);
Global::RefLocalPasscodeChanged().notify();

View File

@ -97,6 +97,7 @@ private:
std::unique_ptr<Settings> _storedSettings;
MTP::Instance::Config _mtpConfig;
MTP::AuthKeysList _mtpKeysToDestroy;
bool _loggingOut = false;
rpl::lifetime _lifetime;

View File

@ -40,6 +40,7 @@ constexpr auto kTemporaryExpiresIn = TimeId(10);
constexpr auto kBindKeyAdditionalExpiresTimeout = TimeId(30);
constexpr auto kTestModeDcIdShift = 10000;
constexpr auto kCheckSentRequestsEach = 1 * crl::time(1000);
constexpr auto kKeyOldEnoughForDestroy = 60 * crl::time(1000);
// If we can't connect for this time we will ask _instance to update config.
constexpr auto kRequestConfigTimeout = 8 * crl::time(1000);
@ -997,7 +998,7 @@ void ConnectionPrivate::connectToServer(bool afterConfig) {
if (_testConnections.empty()) {
if (_instance->isKeysDestroyer()) {
LOG(("MTP Error: DC %1 options for not found for auth key destruction!").arg(_shiftedDcId));
_instance->checkIfKeyWasDestroyed(_shiftedDcId);
_instance->keyWasPossiblyDestroyed(_shiftedDcId);
return;
} else if (afterConfig) {
LOG(("MTP Error: DC %1 options for not found right after config load!").arg(_shiftedDcId));
@ -1857,19 +1858,19 @@ ConnectionPrivate::HandleResult ConnectionPrivate::handleOneReceived(const mtpPr
const auto result = _keyCreator->handleBindResponse(
reqMsgId,
response);
if (result == DcKeyBindState::Success) {
switch (result) {
case DcKeyBindState::Success:
_sessionData->releaseKeyCreationOnDone(
_encryptionKey,
base::take(_keyCreator)->bindPersistentKey());
_sessionData->queueNeedToResumeAndSend();
return HandleResult::Success;
} else if (result == DcKeyBindState::Failed
|| result == DcKeyBindState::DefinitelyDestroyed) {
// #TODO maybe destroy persistent key
// crl::on_main(
// _keyCreator->bindPersistentKey()->setLastCheckTime(crl::now());
// instance->keyDestroyedOnServer(BareDcId(shiftedDcId), base::take(_keyCreator)->bindPersistentKey()->keyId());
// )
case DcKeyBindState::DefinitelyDestroyed:
if (destroyOldEnoughPersistentKey()) {
return HandleResult::DestroyTemporaryKey;
}
[[fallthrough]];
case DcKeyBindState::Failed:
_sessionData->queueNeedToResumeAndSend();
return HandleResult::Success;
}
@ -2362,7 +2363,7 @@ void ConnectionPrivate::applyAuthKey(AuthKeyPtr &&encryptionKey) {
if (_instance->isKeysDestroyer()) {
// We are here to destroy an old key, so we're done.
LOG(("MTP Error: No key %1 in updateAuthKey() for destroying.").arg(_shiftedDcId));
_instance->checkIfKeyWasDestroyed(_shiftedDcId);
_instance->keyWasPossiblyDestroyed(_shiftedDcId);
} else if (_keyCreator) {
DEBUG_LOG(("AuthKey Info: No key in updateAuthKey(), creating."));
_keyCreator->start(
@ -2375,6 +2376,25 @@ void ConnectionPrivate::applyAuthKey(AuthKeyPtr &&encryptionKey) {
}
}
bool ConnectionPrivate::destroyOldEnoughPersistentKey() {
Expects(_keyCreator != nullptr);
const auto key = _keyCreator->bindPersistentKey();
Assert(key != nullptr);
const auto created = key->creationTime();
if (created > 0 && crl::now() - created < kKeyOldEnoughForDestroy) {
return false;
}
const auto instance = _instance;
const auto shiftedDcId = _shiftedDcId;
const auto keyId = key->keyId();
InvokeQueued(instance, [=] {
instance->keyDestroyedOnServer(shiftedDcId, keyId);
});
return true;
}
void ConnectionPrivate::tryAcquireKeyCreation() {
if (_instance->isKeysDestroyer()
|| _keyCreator
@ -2436,7 +2456,6 @@ void ConnectionPrivate::tryAcquireKeyCreation() {
onReceivedSome();
};
// const auto check = (GetDcIdShift(_shiftedDcId) == kCheckKeyDcShift); // #TODO remove kCheckKeyDcShift
auto request = DcKeyRequest();
request.persistentNeeded = !_sessionData->getPersistentKey();
request.temporaryExpiresIn = kTemporaryExpiresIn;
@ -2493,7 +2512,7 @@ void ConnectionPrivate::handleError(int errorCode) {
void ConnectionPrivate::destroyTemporaryKey() {
if (_instance->isKeysDestroyer()) {
LOG(("MTP Info: -404 error received in destroyer %1, assuming key was destroyed.").arg(_shiftedDcId));
_instance->checkIfKeyWasDestroyed(_shiftedDcId);
_instance->keyWasPossiblyDestroyed(_shiftedDcId);
return;
}
LOG(("MTP Info: -404 error received in %1 with temporary key, assuming it was destroyed.").arg(_shiftedDcId));

View File

@ -189,6 +189,7 @@ private:
void clearUnboundKeyCreator();
void releaseKeyCreationOnFail();
void applyAuthKey(AuthKeyPtr &&encryptionKey);
bool destroyOldEnoughPersistentKey();
void setCurrentKeyId(uint64 newKeyId);
void changeSessionId();

View File

@ -42,7 +42,6 @@ constexpr auto kLogoutDcShift = 0x02;
constexpr auto kUpdaterDcShift = 0x03;
constexpr auto kExportDcShift = 0x04;
constexpr auto kExportMediaDcShift = 0x05;
constexpr auto kCheckKeyDcShift = 0x06;
constexpr auto kMaxMediaDcCount = 0x10;
constexpr auto kBaseDownloadDcShift = 0x10;
constexpr auto kBaseUploadDcShift = 0x20;

View File

@ -95,7 +95,7 @@ public:
void killSession(ShiftedDcId shiftedDcId);
void stopSession(ShiftedDcId shiftedDcId);
void reInitConnection(DcId dcId);
void logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail);
void logout(Fn<void()> done);
not_null<Dcenter*> getDcById(ShiftedDcId shiftedDcId);
Dcenter *findDc(ShiftedDcId shiftedDcId);
@ -154,11 +154,10 @@ public:
}
void scheduleKeyDestroy(ShiftedDcId shiftedDcId);
void checkIfKeyWasDestroyed(ShiftedDcId shiftedDcId);
void keyWasPossiblyDestroyed(ShiftedDcId shiftedDcId);
void performKeyDestroy(ShiftedDcId shiftedDcId);
void completedKeyDestroy(ShiftedDcId shiftedDcId);
void checkMainDcKey();
void keyDestroyedOnServer(DcId dcId, uint64 keyId);
void keyDestroyedOnServer(ShiftedDcId shiftedDcId, uint64 keyId);
void prepareToDestroy();
@ -609,10 +608,13 @@ void Instance::Private::reInitConnection(DcId dcId) {
}
}
void Instance::Private::logout(
RPCDoneHandlerPtr onDone,
RPCFailHandlerPtr onFail) {
_instance->send(MTPauth_LogOut(), std::move(onDone), std::move(onFail));
void Instance::Private::logout(Fn<void()> done) {
_instance->send(MTPauth_LogOut(), rpcDone([=] {
done();
}), rpcFail([=] {
done();
return true;
}));
logoutGuestDcs();
}
@ -1502,14 +1504,16 @@ Session *Instance::Private::findSession(ShiftedDcId shiftedDcId) {
not_null<Session*> Instance::Private::startSession(ShiftedDcId shiftedDcId) {
Expects(BareDcId(shiftedDcId) != 0);
const auto dc = (GetDcIdShift(shiftedDcId) != kCheckKeyDcShift)
? getDcById(shiftedDcId).get()
: nullptr;
const auto dc = getDcById(shiftedDcId);
const auto result = _sessions.emplace(
shiftedDcId,
std::make_unique<Session>(_instance, shiftedDcId, dc)
).first->second.get();
result->start();
if (isKeysDestroyer()) {
scheduleKeyDestroy(shiftedDcId);
}
return result;
}
@ -1542,24 +1546,20 @@ void Instance::Private::scheduleKeyDestroy(ShiftedDcId shiftedDcId) {
}
}
void Instance::Private::checkIfKeyWasDestroyed(ShiftedDcId shiftedDcId) {
void Instance::Private::keyWasPossiblyDestroyed(ShiftedDcId shiftedDcId) {
Expects(isKeysDestroyer());
InvokeQueued(_instance, [=] {
if (isKeysDestroyer()) {
LOG(("MTP Info: checkIfKeyWasDestroyed on destroying key %1, "
"assuming it is destroyed.").arg(shiftedDcId));
completedKeyDestroy(shiftedDcId);
} else if (BareDcId(shiftedDcId) == mainDcId()) {
LOG(("MTP Info: checkIfKeyWasDestroyed for main dc %1, "
"checking.").arg(shiftedDcId));
checkMainDcKey();
}
LOG(("MTP Info: checkIfKeyWasDestroyed on destroying key %1, "
"assuming it is destroyed.").arg(shiftedDcId));
completedKeyDestroy(shiftedDcId);
});
}
void Instance::Private::performKeyDestroy(ShiftedDcId shiftedDcId) {
Expects(isKeysDestroyer());
_instance->send(MTPDestroy_auth_key(), rpcDone([this, shiftedDcId](const MTPDestroyAuthKeyRes &result) {
_instance->send(MTPDestroy_auth_key(), rpcDone([=](const MTPDestroyAuthKeyRes &result) {
switch (result.type()) {
case mtpc_destroy_auth_key_ok: LOG(("MTP Info: key %1 destroyed.").arg(shiftedDcId)); break;
case mtpc_destroy_auth_key_fail: {
@ -1568,10 +1568,10 @@ void Instance::Private::performKeyDestroy(ShiftedDcId shiftedDcId) {
} break;
case mtpc_destroy_auth_key_none: LOG(("MTP Info: key %1 already destroyed.").arg(shiftedDcId)); break;
}
_instance->checkIfKeyWasDestroyed(shiftedDcId);
}), rpcFail([this, shiftedDcId](const RPCError &error) {
_instance->keyWasPossiblyDestroyed(shiftedDcId);
}), rpcFail([=](const RPCError &error) {
LOG(("MTP Error: key %1 destruction resulted in error: %2").arg(shiftedDcId).arg(error.type()));
_instance->checkIfKeyWasDestroyed(shiftedDcId);
_instance->keyWasPossiblyDestroyed(shiftedDcId);
return true;
}), shiftedDcId);
}
@ -1587,34 +1587,19 @@ void Instance::Private::completedKeyDestroy(ShiftedDcId shiftedDcId) {
}
}
void Instance::Private::checkMainDcKey() {
const auto id = mainDcId();
const auto shiftedDcId = ShiftDcId(id, kCheckKeyDcShift);
if (findSession(shiftedDcId)) {
return;
}
const auto i = _keysForWrite.find(id);
if (i == end(_keysForWrite)) {
return;
}
const auto lastCheckTime = i->second->lastCheckTime();
if (lastCheckTime > 0 && lastCheckTime + kCheckKeyEach >= crl::now()) {
return;
}
_instance->sendDcKeyCheck(shiftedDcId, i->second);
}
void Instance::Private::keyDestroyedOnServer(DcId dcId, uint64 keyId) {
LOG(("Destroying key for dc: %1").arg(dcId));
if (const auto dc = findDc(dcId)) {
void Instance::Private::keyDestroyedOnServer(
ShiftedDcId shiftedDcId,
uint64 keyId) {
LOG(("Destroying key for dc: %1").arg(shiftedDcId));
if (const auto dc = findDc(BareDcId(shiftedDcId))) {
if (dc->destroyConfirmedForgottenKey(keyId)) {
LOG(("Key destroyed!"));
dcPersistentKeyChanged(dcId, nullptr);
dcPersistentKeyChanged(BareDcId(shiftedDcId), nullptr);
} else {
LOG(("Key already is different."));
}
}
restart(dcId);
restart(shiftedDcId);
}
void Instance::Private::setUpdatesHandler(RPCDoneHandlerPtr onDone) {
@ -1758,8 +1743,8 @@ void Instance::reInitConnection(DcId dcId) {
_private->reInitConnection(dcId);
}
void Instance::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail) {
_private->logout(onDone, onFail);
void Instance::logout(Fn<void()> done) {
_private->logout(std::move(done));
}
void Instance::dcPersistentKeyChanged(
@ -1857,16 +1842,12 @@ bool Instance::isKeysDestroyer() const {
return _private->isKeysDestroyer();
}
void Instance::scheduleKeyDestroy(ShiftedDcId shiftedDcId) {
_private->scheduleKeyDestroy(shiftedDcId);
void Instance::keyWasPossiblyDestroyed(ShiftedDcId shiftedDcId) {
_private->keyWasPossiblyDestroyed(shiftedDcId);
}
void Instance::checkIfKeyWasDestroyed(ShiftedDcId shiftedDcId) {
_private->checkIfKeyWasDestroyed(shiftedDcId);
}
void Instance::keyDestroyedOnServer(DcId dcId, uint64 keyId) {
_private->keyDestroyedOnServer(dcId, keyId);
void Instance::keyDestroyedOnServer(ShiftedDcId shiftedDcId, uint64 keyId) {
_private->keyDestroyedOnServer(shiftedDcId, keyId);
}
void Instance::sendRequest(
@ -1891,10 +1872,6 @@ void Instance::sendAnything(ShiftedDcId shiftedDcId, crl::time msCanWait) {
_private->getSession(shiftedDcId)->sendAnything(msCanWait);
}
void Instance::sendDcKeyCheck(ShiftedDcId shiftedDcId, const AuthKeyPtr &key) {
_private->getSession(shiftedDcId)->sendDcKeyCheck(key);
}
Instance::~Instance() {
_private->prepareToDestroy();
}

View File

@ -85,7 +85,7 @@ public:
void killSession(ShiftedDcId shiftedDcId);
void stopSession(ShiftedDcId shiftedDcId);
void reInitConnection(DcId dcId);
void logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail);
void logout(Fn<void()> done);
void unpaused();
@ -110,12 +110,12 @@ public:
// return true if need to clean request data
bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err);
// Thread-safe.
bool isKeysDestroyer() const;
void scheduleKeyDestroy(ShiftedDcId shiftedDcId);
void checkIfKeyWasDestroyed(ShiftedDcId shiftedDcId);
void keyWasPossiblyDestroyed(ShiftedDcId shiftedDcId);
// Main thread.
void keyDestroyedOnServer(DcId dcId, uint64 keyId);
void keyDestroyedOnServer(ShiftedDcId shiftedDcId, uint64 keyId);
void requestConfig();
void requestConfigIfOld();
@ -128,7 +128,6 @@ public:
void connectionFinished(not_null<internal::Connection*> connection);
void sendAnything(ShiftedDcId shiftedDcId = 0, crl::time msCanWait = 0);
void sendDcKeyCheck(ShiftedDcId shiftedDcId, const AuthKeyPtr &key);
template <typename Request>
mtpRequestId send(

View File

@ -19,7 +19,7 @@ AuthKey::AuthKey(Type type, DcId dcId, const Data &data)
, _key(data) {
countKeyId();
if (type == Type::Generated || type == Type::Temporary) {
_lastCheckTime = crl::now();
_creationTime = crl::now();
}
}
@ -115,12 +115,8 @@ bool AuthKey::equals(const std::shared_ptr<AuthKey> &other) const {
return other ? (_key == other->_key) : false;
}
crl::time AuthKey::lastCheckTime() const {
return _lastCheckTime;
}
void AuthKey::setLastCheckTime(crl::time time) {
_lastCheckTime = time;
crl::time AuthKey::creationTime() const {
return _creationTime;
}
TimeId AuthKey::expiresAt() const {

View File

@ -44,8 +44,7 @@ public:
[[nodiscard]] bytes::const_span data() const;
[[nodiscard]] bool equals(const std::shared_ptr<AuthKey> &other) const;
[[nodiscard]] crl::time lastCheckTime() const;
void setLastCheckTime(crl::time time);
[[nodiscard]] crl::time creationTime() const; // > 0 if known.
[[nodiscard]] TimeId expiresAt() const;
void setExpiresAt(TimeId expiresAt);
@ -58,7 +57,7 @@ private:
DcId _dcId = 0;
Data _key = { { gsl::byte{} } };
KeyId _keyId = 0;
crl::time _lastCheckTime = 0;
crl::time _creationTime = 0;
TimeId _expiresAt = 0;
};

View File

@ -50,10 +50,6 @@ void SessionData::withSession(Callback &&callback) {
}
}
void SessionData::setKeyForCheck(const AuthKeyPtr &key) {
_dcKeyForCheck = key;
}
void SessionData::notifyConnectionInited(const ConnectionOptions &options) {
// #TODO race
const auto current = connectionOptions();
@ -61,7 +57,10 @@ void SessionData::notifyConnectionInited(const ConnectionOptions &options) {
&& current.systemLangCode == _options.systemLangCode
&& current.langPackName == _options.langPackName
&& current.proxy == _options.proxy) {
owner()->notifyDcConnectionInited();
QMutexLocker lock(&_ownerMutex);
if (_owner) {
_owner->notifyDcConnectionInited();
}
}
}
@ -211,19 +210,16 @@ void SessionData::detach() {
Session::Session(
not_null<Instance*> instance,
ShiftedDcId shiftedDcId,
Dcenter *dc)
not_null<Dcenter*> dc)
: QObject()
, _instance(instance)
, _shiftedDcId(shiftedDcId)
, _ownedDc(dc ? nullptr : std::make_unique<Dcenter>(shiftedDcId, nullptr))
, _dc(dc ? dc : _ownedDc.get())
, _dc(dc)
, _data(std::make_shared<SessionData>(this))
, _sender([=] { needToResumeAndSend(); }) {
_timeouter.callEach(1000);
refreshOptions();
if (sharedDc()) {
watchDcKeyChanges();
}
watchDcKeyChanges();
}
void Session::watchDcKeyChanges() {
@ -240,9 +236,6 @@ void Session::watchDcKeyChanges() {
void Session::start() {
_connection = std::make_unique<Connection>(_instance);
_connection->start(_data, _shiftedDcId);
if (_instance->isKeysDestroyer()) {
_instance->scheduleKeyDestroy(_shiftedDcId);
}
}
bool Session::rpcErrorOccured(
@ -317,11 +310,6 @@ void Session::unpaused() {
}
}
void Session::sendDcKeyCheck(const AuthKeyPtr &key) {
_data->setKeyForCheck(key);
needToResumeAndSend();
}
void Session::sendAnything(crl::time msCanWait) {
if (_killed) {
DEBUG_LOG(("Session Error: can't send anything in a killed session"));
@ -381,10 +369,6 @@ void Session::sendMsgsStateInfo(quint64 msgId, QByteArray data) {
MTP_msgs_state_info(MTP_long(msgId), MTP_bytes(data))));
}
bool Session::sharedDc() const {
return (_ownedDc == nullptr);
}
void Session::connectionStateChange(int newState) {
_instance->onStateChange(_shiftedDcId, newState);
}
@ -565,17 +549,15 @@ void Session::releaseKeyCreationOnDone(
_dc->releaseKeyCreationOnDone(temporaryKey, persistentKey);
_myKeyCreation = false;
if (sharedDc()) {
const auto dcId = _dc->id();
const auto instance = _instance;
InvokeQueued(instance, [=] {
if (persistentKey) {
instance->dcPersistentKeyChanged(dcId, persistentKey);
} else {
instance->dcTemporaryKeyChanged(dcId);
}
});
}
const auto dcId = _dc->id();
const auto instance = _instance;
InvokeQueued(instance, [=] {
if (persistentKey) {
instance->dcPersistentKeyChanged(dcId, persistentKey);
} else {
instance->dcTemporaryKeyChanged(dcId);
}
});
}
void Session::releaseKeyCreationOnFail() {
@ -594,13 +576,11 @@ void Session::destroyTemporaryKey(uint64 keyId) {
if (!_dc->destroyTemporaryKey(keyId)) {
return;
}
if (sharedDc()) {
const auto dcId = _dc->id();
const auto instance = _instance;
InvokeQueued(instance, [=] {
instance->dcTemporaryKeyChanged(dcId);
});
}
const auto dcId = _dc->id();
const auto instance = _instance;
InvokeQueued(instance, [=] {
instance->dcTemporaryKeyChanged(dcId);
});
}
int32 Session::getDcWithShift() const {

View File

@ -93,11 +93,6 @@ public:
return _options;
}
[[nodiscard]] const AuthKeyPtr &getKeyForCheck() const {
return _dcKeyForCheck;
}
void setKeyForCheck(const AuthKeyPtr &key);
not_null<QReadWriteLock*> toSendMutex() const {
return &_toSendLock;
}
@ -190,7 +185,6 @@ private:
Session *_owner = nullptr;
mutable QMutex _ownerMutex;
AuthKeyPtr _dcKeyForCheck;
ConnectionOptions _options;
PreRequestMap _toSend; // map of request_id -> request, that is waiting to be sent
@ -219,7 +213,7 @@ public:
Session(
not_null<Instance*> instance,
ShiftedDcId shiftedDcId,
Dcenter *dc);
not_null<Dcenter*> dc);
~Session();
void start();
@ -266,8 +260,6 @@ public:
int32 getState() const;
QString transport() const;
void sendDcKeyCheck(const AuthKeyPtr &key);
void tryToReceive();
void needToResumeAndSend();
void connectionStateChange(int newState);
@ -282,14 +274,12 @@ signals:
void needToRestart();
private:
[[nodiscard]] bool sharedDc() const;
void watchDcKeyChanges();
bool rpcErrorOccured(mtpRequestId requestId, const RPCFailHandlerPtr &onFail, const RPCError &err);
const not_null<Instance*> _instance;
const ShiftedDcId _shiftedDcId = 0;
const std::unique_ptr<Dcenter> _ownedDc;
const not_null<Dcenter*> _dc;
const std::shared_ptr<SessionData> _data;