lib/model: Fix passwords on receive-enc needing token (ref #7518) (#7739)

This commit is contained in:
Simon Frei 2021-06-03 15:39:49 +02:00 committed by GitHub
parent 52eb7392c4
commit 41baccb85d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 38 additions and 43 deletions

View File

@ -192,12 +192,9 @@ var (
errEncryptionPassword = errors.New("different encryption passwords used") errEncryptionPassword = errors.New("different encryption passwords used")
errEncryptionTokenRead = errors.New("failed to read encryption token") errEncryptionTokenRead = errors.New("failed to read encryption token")
errEncryptionTokenWrite = errors.New("failed to write encryption token") errEncryptionTokenWrite = errors.New("failed to write encryption token")
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,
@ -1514,15 +1511,7 @@ 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, err := m.generateClusterConfig(conn.ID()) cm, passwords := 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)
} }
@ -2260,12 +2249,7 @@ 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, err := m.generateClusterConfig(deviceID) cm, passwords := 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)
@ -2428,7 +2412,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, error) { func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.ClusterConfig, map[string]string) {
var message protocol.ClusterConfig var message protocol.ClusterConfig
m.fmut.RLock() m.fmut.RLock()
@ -2441,15 +2425,11 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.Cluste
continue continue
} }
var encryptionToken []byte encryptionToken, hasEncryptionToken := m.folderEncryptionPasswordTokens[folderCfg.ID]
var hasEncryptionToken bool if folderCfg.Type == config.FolderTypeReceiveEncrypted && !hasEncryptionToken {
if folderCfg.Type == config.FolderTypeReceiveEncrypted { // We haven't gotten a token for us yet and without one the other
if encryptionToken, hasEncryptionToken = m.folderEncryptionPasswordTokens[folderCfg.ID]; !hasEncryptionToken { // side can't validate us - pretend we don't have the folder yet.
// We haven't gotten a token yet and without one the other side continue
// can't validate us - reset the connection to trigger a new
// cluster-config and get the token.
return message, nil, errEncryptionNeedToken
}
} }
protocolFolder := protocol.Folder{ protocolFolder := protocol.Folder{
@ -2506,7 +2486,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, nil return message, passwords
} }
func (m *model) State(folder string) (string, time.Time, error) { func (m *model) State(folder string) (string, time.Time, error) {
@ -2798,6 +2778,7 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
// Tracks devices affected by any configuration change to resend ClusterConfig. // Tracks devices affected by any configuration change to resend ClusterConfig.
clusterConfigDevices := make(deviceIDSet, len(from.Devices)+len(to.Devices)) clusterConfigDevices := make(deviceIDSet, len(from.Devices)+len(to.Devices))
closeDevices := make([]protocol.DeviceID, 0, len(to.Devices))
fromFolders := mapFolders(from.Folders) fromFolders := mapFolders(from.Folders)
toFolders := mapFolders(to.Folders) toFolders := mapFolders(to.Folders)
@ -2840,7 +2821,23 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
return true return true
} }
clusterConfigDevices.add(fromCfg.DeviceIDs()) clusterConfigDevices.add(fromCfg.DeviceIDs())
if toCfg.Type != config.FolderTypeReceiveEncrypted {
clusterConfigDevices.add(toCfg.DeviceIDs()) clusterConfigDevices.add(toCfg.DeviceIDs())
} else {
// If we don't have the encryption token yet, we need to drop
// the connection to make the remote re-send the cluster-config
// and with it the token.
m.fmut.RLock()
_, ok := m.folderEncryptionPasswordTokens[toCfg.ID]
m.fmut.RUnlock()
if !ok {
for _, id := range toCfg.DeviceIDs() {
closeDevices = append(closeDevices, id)
}
} else {
clusterConfigDevices.add(toCfg.DeviceIDs())
}
}
} }
// Emit the folder pause/resume event // Emit the folder pause/resume event
@ -2863,7 +2860,6 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
// Pausing a device, unpausing is handled by the connection service. // Pausing a device, unpausing is handled by the connection service.
fromDevices := from.DeviceMap() fromDevices := from.DeviceMap()
toDevices := to.DeviceMap() toDevices := to.DeviceMap()
closeDevices := make([]protocol.DeviceID, 0, len(to.Devices))
for deviceID, toCfg := range toDevices { for deviceID, toCfg := range toDevices {
fromCfg, ok := fromDevices[deviceID] fromCfg, ok := fromDevices[deviceID]
if !ok { if !ok {
@ -2878,17 +2874,17 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
continue continue
} }
// Ignored folder was removed, reconnect to retrigger the prompt.
if !toCfg.Paused && len(fromCfg.IgnoredFolders) > len(toCfg.IgnoredFolders) {
closeDevices = append(closeDevices, deviceID)
}
if toCfg.Paused { if toCfg.Paused {
l.Infoln("Pausing", deviceID) l.Infoln("Pausing", deviceID)
closeDevices = append(closeDevices, deviceID) closeDevices = append(closeDevices, deviceID)
delete(clusterConfigDevices, deviceID)
m.evLogger.Log(events.DevicePaused, map[string]string{"device": deviceID.String()}) m.evLogger.Log(events.DevicePaused, map[string]string{"device": deviceID.String()})
} else { } else {
// Ignored folder was removed, reconnect to retrigger the prompt.
if len(fromCfg.IgnoredFolders) > len(toCfg.IgnoredFolders) {
closeDevices = append(closeDevices, deviceID)
}
l.Infoln("Resuming", deviceID)
m.evLogger.Log(events.DeviceResumed, map[string]string{"device": deviceID.String()}) m.evLogger.Log(events.DeviceResumed, map[string]string{"device": deviceID.String()})
} }
} }
@ -2904,11 +2900,13 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
m.pmut.RLock() m.pmut.RLock()
for _, id := range closeDevices { for _, id := range closeDevices {
delete(clusterConfigDevices, id)
if conn, ok := m.conn[id]; ok { if conn, ok := m.conn[id]; ok {
go conn.Close(errDevicePaused) go conn.Close(errDevicePaused)
} }
} }
for _, id := range removedDevices { for _, id := range removedDevices {
delete(clusterConfigDevices, id)
if conn, ok := m.conn[id]; ok { if conn, ok := m.conn[id]; ok {
go conn.Close(errDeviceRemoved) go conn.Close(errDeviceRemoved)
} }

View File

@ -430,8 +430,7 @@ func TestClusterConfig(t *testing.T) {
m.ServeBackground() m.ServeBackground()
defer cleanupModel(m) defer cleanupModel(m)
cm, _, err := m.generateClusterConfig(device2) cm, _ := 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)
@ -868,8 +867,7 @@ func TestIssue4897(t *testing.T) {
defer cleanupModel(m) defer cleanupModel(m)
cancel() cancel()
cm, _, err := m.generateClusterConfig(device1) cm, _ := 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)
} }
@ -4160,8 +4158,7 @@ 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, _, err := m.generateClusterConfig(device1) cc, _ := 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)
} }