Browse Source

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

Simon Frei 4 years ago
parent
commit
41baccb85d
2 changed files with 38 additions and 43 deletions
  1. 35 37
      lib/model/model.go
  2. 3 6
      lib/model/model_test.go

+ 35 - 37
lib/model/model.go

@@ -192,12 +192,9 @@ var (
 	errEncryptionPassword                 = errors.New("different encryption passwords used")
 	errEncryptionTokenRead                = errors.New("failed to read 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")
 	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,
@@ -1514,15 +1511,7 @@ 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, err := m.generateClusterConfig(conn.ID())
-		if err != nil {
-			if err != errEncryptionNeedToken {
-				m.evLogger.Log(events.Failure, failureUnexpectedGenerateCCError)
-				continue
-			}
-			go conn.Close(err)
-			continue
-		}
+		cm, passwords := m.generateClusterConfig(conn.ID())
 		conn.SetFolderPasswords(passwords)
 		go conn.ClusterConfig(cm)
 	}
@@ -2260,12 +2249,7 @@ 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, 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)
-	}
+	cm, passwords := m.generateClusterConfig(deviceID)
 	conn.SetFolderPasswords(passwords)
 	conn.ClusterConfig(cm)
 
@@ -2428,7 +2412,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, error) {
+func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.ClusterConfig, map[string]string) {
 	var message protocol.ClusterConfig
 
 	m.fmut.RLock()
@@ -2441,15 +2425,11 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.Cluste
 			continue
 		}
 
-		var encryptionToken []byte
-		var hasEncryptionToken bool
-		if folderCfg.Type == config.FolderTypeReceiveEncrypted {
-			if encryptionToken, hasEncryptionToken = m.folderEncryptionPasswordTokens[folderCfg.ID]; !hasEncryptionToken {
-				// 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
-			}
+		encryptionToken, hasEncryptionToken := m.folderEncryptionPasswordTokens[folderCfg.ID]
+		if folderCfg.Type == config.FolderTypeReceiveEncrypted && !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
 		}
 
 		protocolFolder := protocol.Folder{
@@ -2506,7 +2486,7 @@ func (m *model) generateClusterConfig(device protocol.DeviceID) (protocol.Cluste
 		message.Folders = append(message.Folders, protocolFolder)
 	}
 
-	return message, passwords, nil
+	return message, passwords
 }
 
 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.
 	clusterConfigDevices := make(deviceIDSet, len(from.Devices)+len(to.Devices))
+	closeDevices := make([]protocol.DeviceID, 0, len(to.Devices))
 
 	fromFolders := mapFolders(from.Folders)
 	toFolders := mapFolders(to.Folders)
@@ -2840,7 +2821,23 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
 				return true
 			}
 			clusterConfigDevices.add(fromCfg.DeviceIDs())
-			clusterConfigDevices.add(toCfg.DeviceIDs())
+			if toCfg.Type != config.FolderTypeReceiveEncrypted {
+				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
@@ -2863,7 +2860,6 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
 	// Pausing a device, unpausing is handled by the connection service.
 	fromDevices := from.DeviceMap()
 	toDevices := to.DeviceMap()
-	closeDevices := make([]protocol.DeviceID, 0, len(to.Devices))
 	for deviceID, toCfg := range toDevices {
 		fromCfg, ok := fromDevices[deviceID]
 		if !ok {
@@ -2878,17 +2874,17 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
 			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 {
 			l.Infoln("Pausing", deviceID)
 			closeDevices = append(closeDevices, deviceID)
-			delete(clusterConfigDevices, deviceID)
 			m.evLogger.Log(events.DevicePaused, map[string]string{"device": deviceID.String()})
 		} 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()})
 		}
 	}
@@ -2904,11 +2900,13 @@ func (m *model) CommitConfiguration(from, to config.Configuration) bool {
 
 	m.pmut.RLock()
 	for _, id := range closeDevices {
+		delete(clusterConfigDevices, id)
 		if conn, ok := m.conn[id]; ok {
 			go conn.Close(errDevicePaused)
 		}
 	}
 	for _, id := range removedDevices {
+		delete(clusterConfigDevices, id)
 		if conn, ok := m.conn[id]; ok {
 			go conn.Close(errDeviceRemoved)
 		}

+ 3 - 6
lib/model/model_test.go

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