Przeglądaj źródła

Detect nonstandard hash algo and stop folder (ref #2314)

Jakob Borg 9 lat temu
rodzic
commit
0db80710aa

+ 37 - 6
lib/model/model.go

@@ -520,11 +520,7 @@ func (m *Model) Index(deviceID protocol.DeviceID, folder string, fs []protocol.F
 	l.Debugf("IDX(in): %s %q: %d files", deviceID, folder, len(fs))
 
 	if !m.folderSharedWith(folder, deviceID) {
-		events.Default.Log(events.FolderRejected, map[string]string{
-			"folder": folder,
-			"device": deviceID.String(),
-		})
-		l.Infof("Unexpected folder ID %q sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder, deviceID)
+		l.Debugf("Unexpected folder ID %q sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder, deviceID)
 		return
 	}
 
@@ -566,7 +562,7 @@ func (m *Model) IndexUpdate(deviceID protocol.DeviceID, folder string, fs []prot
 	l.Debugf("%v IDXUP(in): %s / %q: %d files", m, deviceID, folder, len(fs))
 
 	if !m.folderSharedWith(folder, deviceID) {
-		l.Infof("Update for unexpected folder ID %q sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder, deviceID)
+		l.Debugf("Update for unexpected folder ID %q sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder, deviceID)
 		return
 	}
 
@@ -596,6 +592,10 @@ func (m *Model) IndexUpdate(deviceID protocol.DeviceID, folder string, fs []prot
 func (m *Model) folderSharedWith(folder string, deviceID protocol.DeviceID) bool {
 	m.fmut.RLock()
 	defer m.fmut.RUnlock()
+	return m.folderSharedWithUnlocked(folder, deviceID)
+}
+
+func (m *Model) folderSharedWithUnlocked(folder string, deviceID protocol.DeviceID) bool {
 	for _, nfolder := range m.deviceFolders[deviceID] {
 		if nfolder == folder {
 			return true
@@ -629,6 +629,37 @@ func (m *Model) ClusterConfig(deviceID protocol.DeviceID, cm protocol.ClusterCon
 
 	m.pmut.Unlock()
 
+	// Check the peer device's announced folders against our own. Emits events
+	// for folders that we don't expect (unknown or not shared).
+
+	m.fmut.Lock()
+nextFolder:
+	for _, folder := range cm.Folders {
+		cfg := m.folderCfgs[folder.ID]
+
+		if _, err := protocol.HashAlgorithmFromFlagBits(folder.Flags); err != nil {
+			// The hash algorithm failed to deserialize, so it's not SHA256
+			// (the only acceptable algorithm).
+			l.Warnf("Device %v: %v", deviceID, err)
+			cfg.Invalid = err.Error() + " from " + deviceID.String()
+			m.cfg.SetFolder(cfg)
+			if srv := m.folderRunners[folder.ID]; srv != nil {
+				srv.setError(fmt.Errorf(cfg.Invalid))
+			}
+			continue nextFolder
+		}
+
+		if !m.folderSharedWithUnlocked(folder.ID, deviceID) {
+			events.Default.Log(events.FolderRejected, map[string]string{
+				"folder": folder.ID,
+				"device": deviceID.String(),
+			})
+			l.Infof("Unexpected folder ID %q sent from device %q; ensure that the folder exists and that this device is selected under \"Share With\" in the folder configuration.", folder.ID, deviceID)
+			continue
+		}
+	}
+	m.fmut.Unlock()
+
 	events.Default.Log(events.DeviceConnected, event)
 
 	l.Infof(`Device %s client is "%s %s" named "%s"`, deviceID, cm.ClientName, cm.ClientVersion, cm.DeviceName)

+ 54 - 0
lib/protocol/hashalgorithm.go

@@ -0,0 +1,54 @@
+// Copyright (C) 2016 The Protocol Authors.
+
+package protocol
+
+import "fmt"
+
+type HashAlgorithm int
+
+const (
+	SHA256 HashAlgorithm = iota
+)
+
+func (h HashAlgorithm) String() string {
+	switch h {
+	case SHA256:
+		return "sha256"
+	default:
+		return "unknown"
+	}
+}
+
+// FlagBits returns the bits that we should or into the folder flag field to
+// indicate the hash algorithm.
+func (h HashAlgorithm) FlagBits() uint32 {
+	switch h {
+	case SHA256:
+		return FolderHashSHA256 << FolderHashShiftBits
+	default:
+		panic("unknown hash algorithm")
+	}
+}
+
+func (h *HashAlgorithm) UnmarshalText(bs []byte) error {
+	switch string(bs) {
+	case "sha256":
+		*h = SHA256
+		return nil
+	}
+	return fmt.Errorf("Unknown hash algorithm %q", string(bs))
+}
+
+func (h *HashAlgorithm) MarshalText() ([]byte, error) {
+	return []byte(h.String()), nil
+}
+
+func HashAlgorithmFromFlagBits(flags uint32) (HashAlgorithm, error) {
+	algo := flags >> FolderHashShiftBits & FolderHashMask
+	switch algo {
+	case FolderHashSHA256:
+		return SHA256, nil
+	default:
+		return 0, fmt.Errorf("Unknown hash algorithm %d", algo)
+	}
+}

+ 35 - 0
lib/protocol/hashalgorithm_test.go

@@ -0,0 +1,35 @@
+// Copyright (C) 2016 The Protocol Authors.
+
+package protocol
+
+import "testing"
+
+/*
+   0                   1                   2                   3
+    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+   |                       Reserved                  | Hash  |D|P|R|
+   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+*/
+func TestHashAlgorithmFromFlagBits(t *testing.T) {
+	// SHA256 is algorithm zero, shifted three bits to the left (for clarity,
+	// I know it doesn't actually do anything).
+
+	sha256 := uint32(0 << 3)
+
+	h, err := HashAlgorithmFromFlagBits(sha256)
+	if err != nil {
+		t.Error(err)
+	}
+	if h != SHA256 {
+		t.Error("Zero should have unmarshalled as SHA256")
+	}
+
+	// Any other algorithm is unknown
+	unknown := uint32(1 << 3)
+
+	_, err = HashAlgorithmFromFlagBits(unknown)
+	if err == nil {
+		t.Error("Unknown algo should not have unmarshalled")
+	}
+}

+ 7 - 0
lib/protocol/protocol.go

@@ -66,6 +66,13 @@ const (
 	FlagFolderReadOnly     uint32 = 1 << 0
 	FlagFolderIgnorePerms         = 1 << 1
 	FlagFolderIgnoreDelete        = 1 << 2
+
+	// The folder hash algorithm IDs, to be put in the flags field by shifting
+	// left FolderHashShiftBits
+	FolderHashSHA256 = 0
+	// ... 1 through 15 currently reserved
+	FolderHashMask      = 15
+	FolderHashShiftBits = 3
 )
 
 // ClusterConfigMessage.Folders.Devices flags