lib/model: Reset conn when enc token is missing (fixes #7198) (#7518)

This commit is contained in:
Simon Frei 2021-03-23 10:38:40 +01:00 committed by GitHub
parent 924b96856f
commit 0d7a77ba85
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 31 additions and 13 deletions

View File

@ -192,10 +192,12 @@ var (
errEncryptionNotEncryptedRemote = errors.New("folder is configured to be encrypted but not announced thus") 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") errEncryptionNotEncryptedUntrusted = errors.New("device is untrusted, but configured to receive not encrypted data")
errEncryptionPassword = errors.New("different encryption passwords used") 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") errMissingRemoteInClusterConfig = errors.New("remote device missing in cluster config")
errMissingLocalInClusterConfig = errors.New("local device missing in cluster config") errMissingLocalInClusterConfig = errors.New("local device missing in cluster config")
errConnLimitReached = errors.New("connection limit reached") 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, // 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 m.folderEncryptionFailures[folder.ID][deviceID] = err
msg := fmt.Sprintf("Failure checking encryption consistency with device %v for folder %v: %v", deviceID, cfg.Description(), 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) l.Debugln(msg)
} else { } else {
l.Warnln(msg) l.Warnln(msg)
@ -1534,7 +1536,15 @@ func (m *model) sendClusterConfig(ids []protocol.DeviceID) {
m.pmut.RUnlock() m.pmut.RUnlock()
// Generating cluster-configs acquires fmut -> must happen outside of pmut. // Generating cluster-configs acquires fmut -> must happen outside of pmut.
for _, conn := range ccConns { 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) conn.SetFolderPasswords(passwords)
go conn.ClusterConfig(cm) go conn.ClusterConfig(cm)
} }
@ -2246,7 +2256,12 @@ func (m *model) AddConnection(conn protocol.Connection, hello protocol.Hello) {
m.pmut.Unlock() m.pmut.Unlock()
// Acquires fmut, so has to be done outside of pmut. // 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.SetFolderPasswords(passwords)
conn.ClusterConfig(cm) conn.ClusterConfig(cm)
@ -2409,7 +2424,7 @@ func (m *model) numHashers(folder string) int {
// generateClusterConfig returns a ClusterConfigMessage that is correct and the // generateClusterConfig returns a ClusterConfigMessage that is correct and the
// set of folder passwords for the given peer device // 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 var message protocol.ClusterConfig
m.fmut.RLock() m.fmut.RLock()
@ -2426,10 +2441,10 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.Cluste
var hasEncryptionToken bool var hasEncryptionToken bool
if folderCfg.Type == config.FolderTypeReceiveEncrypted { if folderCfg.Type == config.FolderTypeReceiveEncrypted {
if encryptionToken, hasEncryptionToken = m.folderEncryptionPasswordTokens[folderCfg.ID]; !hasEncryptionToken { if encryptionToken, hasEncryptionToken = m.folderEncryptionPasswordTokens[folderCfg.ID]; !hasEncryptionToken {
// We haven't gotten a token for us yet and without // We haven't gotten a token yet and without one the other side
// one the other side can't validate us - pretend // can't validate us - reset the connection to trigger a new
// we don't have the folder yet. // cluster-config and get the token.
continue return message, nil, errEncryptionNeedToken
} }
} }
@ -2487,7 +2502,7 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.Cluste
message.Folders = append(message.Folders, protocolFolder) message.Folders = append(message.Folders, protocolFolder)
} }
return message, passwords return message, passwords, nil
} }
func (m *model) State(folder string) (string, time.Time, error) { func (m *model) State(folder string) (string, time.Time, error) {

View File

@ -428,7 +428,8 @@ func TestClusterConfig(t *testing.T) {
m.ServeBackground() m.ServeBackground()
defer cleanupModel(m) defer cleanupModel(m)
cm, _ := m.generateClusterConfig(device2) cm, _, err := m.generateClusterConfig(device2)
must(t, err)
if l := len(cm.Folders); l != 2 { if l := len(cm.Folders); l != 2 {
t.Fatalf("Incorrect number of folders %d != 2", l) t.Fatalf("Incorrect number of folders %d != 2", l)
@ -853,7 +854,8 @@ func TestIssue4897(t *testing.T) {
defer cleanupModel(m) defer cleanupModel(m)
cancel() cancel()
cm, _ := m.generateClusterConfig(device1) cm, _, err := m.generateClusterConfig(device1)
must(t, err)
if l := len(cm.Folders); l != 1 { if l := len(cm.Folders); l != 1 {
t.Errorf("Cluster config contains %v folders, expected 1", l) t.Errorf("Cluster config contains %v folders, expected 1", l)
} }
@ -4142,7 +4144,8 @@ func TestCCFolderNotRunning(t *testing.T) {
defer cleanupModelAndRemoveDir(m, tfs.URI()) defer cleanupModelAndRemoveDir(m, tfs.URI())
// A connection can happen before all the folders are started. // 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 { if l := len(cc.Folders); l != 1 {
t.Fatalf("Expected 1 folder in CC, got %v", l) t.Fatalf("Expected 1 folder in CC, got %v", l)
} }