From 0d7a77ba85c69f9e427981b906c9a4941c4c0f9c Mon Sep 17 00:00:00 2001 From: Simon Frei Date: Tue, 23 Mar 2021 10:38:40 +0100 Subject: [PATCH] lib/model: Reset conn when enc token is missing (fixes #7198) (#7518) --- lib/model/model.go | 35 +++++++++++++++++++++++++---------- lib/model/model_test.go | 9 ++++++--- 2 files changed, 31 insertions(+), 13 deletions(-) diff --git a/lib/model/model.go b/lib/model/model.go index c0024a02b..d73b49800 100644 --- a/lib/model/model.go +++ b/lib/model/model.go @@ -192,10 +192,12 @@ var ( errEncryptionNotEncryptedRemote = errors.New("folder is configured to be encrypted but not announced thus") errEncryptionNotEncryptedUntrusted = errors.New("device is untrusted, but configured to receive not encrypted data") errEncryptionPassword = errors.New("different encryption passwords used") - errEncryptionReceivedToken = errors.New("resetting connection to send info on new encrypted folder (new cluster config)") + errEncryptionNeedToken = errors.New("require password token for receive-encrypted token") errMissingRemoteInClusterConfig = errors.New("remote device missing in cluster config") errMissingLocalInClusterConfig = errors.New("local device missing in cluster config") errConnLimitReached = errors.New("connection limit reached") + // messages for failure reports + failureUnexpectedGenerateCCError = "unexpected error occurred in generateClusterConfig" ) // NewModel creates and starts a new model. The model starts in read-only mode, @@ -1365,7 +1367,7 @@ func (m *model) ccHandleFolders(folders []protocol.Folder, deviceCfg config.Devi } m.folderEncryptionFailures[folder.ID][deviceID] = err msg := fmt.Sprintf("Failure checking encryption consistency with device %v for folder %v: %v", deviceID, cfg.Description(), err) - if sameError || err == errEncryptionReceivedToken { + if sameError { l.Debugln(msg) } else { l.Warnln(msg) @@ -1534,7 +1536,15 @@ func (m *model) sendClusterConfig(ids []protocol.DeviceID) { m.pmut.RUnlock() // Generating cluster-configs acquires fmut -> must happen outside of pmut. for _, conn := range ccConns { - cm, passwords := m.generateClusterConfig(conn.ID()) + cm, passwords, err := m.generateClusterConfig(conn.ID()) + if err != nil { + if err != errEncryptionNeedToken { + m.evLogger.Log(events.Failure, failureUnexpectedGenerateCCError) + continue + } + go conn.Close(err) + continue + } conn.SetFolderPasswords(passwords) go conn.ClusterConfig(cm) } @@ -2246,7 +2256,12 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) { m.pmut.Unlock() // Acquires fmut, so has to be done outside of pmut. - cm, passwords := m.generateClusterConfig(deviceID) + cm, passwords, err := m.generateClusterConfig(deviceID) + // We ignore errEncryptionNeedToken on a new connection, as the missing + // token should be delivered in the cluster-config about to be received. + if err != nil && err != errEncryptionNeedToken { + m.evLogger.Log(events.Failure, failureUnexpectedGenerateCCError) + } conn.SetFolderPasswords(passwords) conn.ClusterConfig(cm) @@ -2409,7 +2424,7 @@ func (m *model) numHashers(folder string) int { // generateClusterConfig returns a ClusterConfigMessage that is correct and the // set of folder passwords for the given peer device -func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.ClusterConfig, map[string]string) { +func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.ClusterConfig, map[string]string, error) { var message protocol.ClusterConfig m.fmut.RLock() @@ -2426,10 +2441,10 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.Cluste var hasEncryptionToken bool if folderCfg.Type == config.FolderTypeReceiveEncrypted { if encryptionToken, hasEncryptionToken = m.folderEncryptionPasswordTokens[folderCfg.ID]; !hasEncryptionToken { - // We haven't gotten a token for us yet and without - // one the other side can't validate us - pretend - // we don't have the folder yet. - continue + // We haven't gotten a token yet and without one the other side + // can't validate us - reset the connection to trigger a new + // cluster-config and get the token. + return message, nil, errEncryptionNeedToken } } @@ -2487,7 +2502,7 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.Cluste message.Folders = append(message.Folders, protocolFolder) } - return message, passwords + return message, passwords, nil } func (m *model) State(folder string) (string, time.Time, error) { diff --git a/lib/model/model_test.go b/lib/model/model_test.go index 096d2aa9b..10135d6f8 100644 --- a/lib/model/model_test.go +++ b/lib/model/model_test.go @@ -428,7 +428,8 @@ func TestClusterConfig(t *testing.T) { m.ServeBackground() defer cleanupModel(m) - cm, _ := m.generateClusterConfig(device2) + cm, _, err := m.generateClusterConfig(device2) + must(t, err) if l := len(cm.Folders); l != 2 { t.Fatalf("Incorrect number of folders %d != 2", l) @@ -853,7 +854,8 @@ func TestIssue4897(t *testing.T) { defer cleanupModel(m) cancel() - cm, _ := m.generateClusterConfig(device1) + cm, _, err := m.generateClusterConfig(device1) + must(t, err) if l := len(cm.Folders); l != 1 { t.Errorf("Cluster config contains %v folders, expected 1", l) } @@ -4142,7 +4144,8 @@ func TestCCFolderNotRunning(t *testing.T) { defer cleanupModelAndRemoveDir(m, tfs.URI()) // A connection can happen before all the folders are started. - cc, _ := m.generateClusterConfig(device1) + cc, _, err := m.generateClusterConfig(device1) + must(t, err) if l := len(cc.Folders); l != 1 { t.Fatalf("Expected 1 folder in CC, got %v", l) }