diff --git a/help/Version History (Changelog).md b/help/Version History (Changelog).md index 7adaf567..b9914586 100644 --- a/help/Version History (Changelog).md +++ b/help/Version History (Changelog).md @@ -1,4 +1,4 @@ -# 2024.04.02 +# 2024.04.05 ## New Features - During recovery or version deletion, real progress is shown @@ -8,6 +8,11 @@ Main idea behind change is to avoid already mounted error. And use it recovery. - Restoration speed was increased in 2 times for Windows. +## Bug Fixes +- FTPS MITM attacks. Be aware that if you had previously setupped FTPS with self-signed certificate, you need to do the following steps: +a. Open task with FTPS in Edit mode, +b. Click Save. + # 2024.03.28 ## Bug Fixes diff --git a/sources/BUtil.Core/BUtil.Core.csproj b/sources/BUtil.Core/BUtil.Core.csproj index dfa3814e..b9a97ea3 100644 --- a/sources/BUtil.Core/BUtil.Core.csproj +++ b/sources/BUtil.Core/BUtil.Core.csproj @@ -26,7 +26,7 @@ - + diff --git a/sources/BUtil.Core/ConfigurationFileModels/V2/FtpsStorageSettingsV2.cs b/sources/BUtil.Core/ConfigurationFileModels/V2/FtpsStorageSettingsV2.cs index 96a92dcd..8808407b 100644 --- a/sources/BUtil.Core/ConfigurationFileModels/V2/FtpsStorageSettingsV2.cs +++ b/sources/BUtil.Core/ConfigurationFileModels/V2/FtpsStorageSettingsV2.cs @@ -10,5 +10,6 @@ public class FtpsStorageSettingsV2 : IStorageSettingsV2 public string? Folder { get; set; } public string User { get; set; } = string.Empty; public string Password { get; set; } = string.Empty; + public string TrustedCertificate { get; set; } = null!; } diff --git a/sources/BUtil.Core/Localization/Resources.Designer.cs b/sources/BUtil.Core/Localization/Resources.Designer.cs index 5b1b988e..11b352cd 100644 --- a/sources/BUtil.Core/Localization/Resources.Designer.cs +++ b/sources/BUtil.Core/Localization/Resources.Designer.cs @@ -607,6 +607,15 @@ public static string Field_Version { } } + /// + /// Looks up a localized string similar to Deleting "{0}". + /// + public static string File_Deleting { + get { + return ResourceManager.GetString("File_Deleting", resourceCulture); + } + } + /// /// Looks up a localized string similar to Integrity verification script.ps1. /// @@ -634,6 +643,15 @@ public static string File_List_Saving { } } + /// + /// Looks up a localized string similar to Moving "{0}" to "{1}". + /// + public static string File_Moving { + get { + return ResourceManager.GetString("File_Moving", resourceCulture); + } + } + /// /// Looks up a localized string similar to Saving "{0}". /// diff --git a/sources/BUtil.Core/Localization/Resources.resx b/sources/BUtil.Core/Localization/Resources.resx index 542c946b..19f6205c 100644 --- a/sources/BUtil.Core/Localization/Resources.resx +++ b/sources/BUtil.Core/Localization/Resources.resx @@ -658,4 +658,10 @@ See detailed description in the following link. User is not specified. + + Deleting "{0}" + + + Moving "{0}" to "{1}" + \ No newline at end of file diff --git a/sources/BUtil.Core/Localization/Resources.ru.resx b/sources/BUtil.Core/Localization/Resources.ru.resx index c7db4355..72f8f7c5 100644 --- a/sources/BUtil.Core/Localization/Resources.ru.resx +++ b/sources/BUtil.Core/Localization/Resources.ru.resx @@ -659,4 +659,10 @@ bin. Пользователь не указан. + + Удаление "{0}" + + + Перемещение "{0}" в "{1}" + \ No newline at end of file diff --git a/sources/BUtil.Core/Storages/FtpsStorage.cs b/sources/BUtil.Core/Storages/FtpsStorage.cs index e5aaef4a..d63d4ffc 100644 --- a/sources/BUtil.Core/Storages/FtpsStorage.cs +++ b/sources/BUtil.Core/Storages/FtpsStorage.cs @@ -5,6 +5,7 @@ using System; using System.IO; using System.Linq; +using System.Net.Security; using System.Security; using System.Text.RegularExpressions; @@ -14,8 +15,9 @@ class FtpsStorage : StorageBase { private readonly string? _normalizedFolder; private readonly FtpClient _client; + private readonly bool _autodetectConnectionSettings; - internal FtpsStorage(ILog log, FtpsStorageSettingsV2 settings) + internal FtpsStorage(ILog log, FtpsStorageSettingsV2 settings, bool autodetectConnectionSettings) : base(log, settings) { if (string.IsNullOrWhiteSpace(Settings.Host)) @@ -30,9 +32,11 @@ internal FtpsStorage(ILog log, FtpsStorageSettingsV2 settings) _normalizedFolder = NormalizeNullablePath(Settings.Folder); _client = Mount(); + _autodetectConnectionSettings = autodetectConnectionSettings; } private readonly object _uploadLock = new(); + public override IStorageUploadResult Upload(string sourceFile, string relativeFileName) { lock (_uploadLock) // because we're limited by upload speed by Server and Internet @@ -84,10 +88,34 @@ private FtpClient Mount() var client = new FtpClient(Settings.Host, Settings.User, Settings.Password, Settings.Port); client.Config.EncryptionMode = GetFtpEncryptionMode(); client.Config.ValidateAnyCertificate = true; + client.ValidateCertificate += OnClientValidateCertificate; client.Connect(); return client; } + private void OnClientValidateCertificate(FluentFTP.Client.BaseClient.BaseFtpClient control, FtpSslValidationEventArgs e) + { + if (e.PolicyErrors == SslPolicyErrors.None) + { + e.Accept = true; + return; + } + + if (_autodetectConnectionSettings) + { + Settings.TrustedCertificate = e.Certificate.GetRawCertDataString(); + } + + if (e.Certificate.GetRawCertDataString() == Settings.TrustedCertificate) + { + e.Accept = true; + return; + } + + Log.WriteLine(LoggingEvent.Error, $"Received certificate from FTPS server violates policy {e.PolicyErrors}. Connection will not be accepted. You're facing Man-in-the-middle attack."); + Log.WriteLine(LoggingEvent.Error, $"If you trust this certificate, open in BUtil this task in Edit mode and click Save. Application will record this certificate as trusted."); + } + private FtpEncryptionMode GetFtpEncryptionMode() { return Settings.Encryption switch diff --git a/sources/BUtil.Core/Storages/StorageFactory.cs b/sources/BUtil.Core/Storages/StorageFactory.cs index 68824bdf..77f74111 100644 --- a/sources/BUtil.Core/Storages/StorageFactory.cs +++ b/sources/BUtil.Core/Storages/StorageFactory.cs @@ -6,14 +6,14 @@ namespace BUtil.Core.Storages; public class StorageFactory { - public static IStorage Create(ILog log, IStorageSettingsV2 storageSettings) + public static IStorage Create(ILog log, IStorageSettingsV2 storageSettings, bool autodetectConnectionSettings) { if (storageSettings is FolderStorageSettingsV2) return new FailoverStorageWrapper(log, new FolderStorage(log, (FolderStorageSettingsV2)storageSettings)); else if (storageSettings is SambaStorageSettingsV2) return new FailoverStorageWrapper(log, PlatformSpecificExperience.Instance.GetSmbStorage(log, (SambaStorageSettingsV2)storageSettings)); else if (storageSettings is FtpsStorageSettingsV2) - return new FailoverStorageWrapper(log, new FtpsStorage(log, (FtpsStorageSettingsV2)storageSettings)); + return new FailoverStorageWrapper(log, new FtpsStorage(log, (FtpsStorageSettingsV2)storageSettings, autodetectConnectionSettings)); else if (storageSettings is MtpStorageSettings) { var mtpStorage = PlatformSpecificExperience.Instance.GetMtpStorage(log, (MtpStorageSettings)storageSettings); @@ -34,7 +34,7 @@ public static IStorage Create(ILog log, IStorageSettingsV2 storageSettings) try { - using var storage = Create(log, storageSettings); + using var storage = Create(log, storageSettings, true); return storage.Test(); } catch (Exception ex) diff --git a/sources/BUtil.Core/TasksTree/ImportMedia/ImportFilesTask.cs b/sources/BUtil.Core/TasksTree/ImportMedia/ImportFilesTask.cs index 07cbe0f1..ed184e03 100644 --- a/sources/BUtil.Core/TasksTree/ImportMedia/ImportFilesTask.cs +++ b/sources/BUtil.Core/TasksTree/ImportMedia/ImportFilesTask.cs @@ -36,8 +36,8 @@ public override void Execute() var importMediaFileService = new ImportMediaFileService(); var importMediaState = options.SkipAlreadyImportedFiles ? importMediaFileService.Load(_task.Name) ?? new ImportMediaState() : new ImportMediaState(); - var fromStorage = StorageFactory.Create(this.Log, options.From); - var toStorage = StorageFactory.Create(this.Log, new FolderStorageSettingsV2 { DestinationFolder = options.DestinationFolder }); + var fromStorage = StorageFactory.Create(this.Log, options.From, false); + var toStorage = StorageFactory.Create(this.Log, new FolderStorageSettingsV2 { DestinationFolder = options.DestinationFolder }, false); var transformFileName = options.TransformFileName; var fromStorageFiles = fromStorage.GetFiles(null, SearchOption.AllDirectories); diff --git a/sources/BUtil.Core/TasksTree/IncrementalModel/StorageSpecificServicesIoc.cs b/sources/BUtil.Core/TasksTree/IncrementalModel/StorageSpecificServicesIoc.cs index db976166..b001e25f 100644 --- a/sources/BUtil.Core/TasksTree/IncrementalModel/StorageSpecificServicesIoc.cs +++ b/sources/BUtil.Core/TasksTree/IncrementalModel/StorageSpecificServicesIoc.cs @@ -21,11 +21,11 @@ public class StorageSpecificServicesIoc : IDisposable private readonly Lazy _incrementalBackupFileService; public IncrementalBackupFileService IncrementalBackupFileService { get { return _incrementalBackupFileService.Value; } } - public StorageSpecificServicesIoc(ILog log, IStorageSettingsV2 storageSettings, IHashService hashService) + public StorageSpecificServicesIoc(ILog log, IStorageSettingsV2 storageSettings, IHashService hashService, bool autodetectConnectionSettings = false) { Log = log; StorageSettings = storageSettings; - _storage = new Lazy(() => StorageFactory.Create(log, storageSettings)); + _storage = new Lazy(() => StorageFactory.Create(log, storageSettings, autodetectConnectionSettings)); _incrementalBackupStateService = new Lazy(() => new IncrementalBackupStateService(this, hashService)); _incrementalBackupFileService = new Lazy(() => new IncrementalBackupFileService(hashService, this)); } diff --git a/sources/BUtil.Core/TasksTree/Storage/DeleteStorageFileTask.cs b/sources/BUtil.Core/TasksTree/Storage/DeleteStorageFileTask.cs index ee35ff39..cf8195e9 100644 --- a/sources/BUtil.Core/TasksTree/Storage/DeleteStorageFileTask.cs +++ b/sources/BUtil.Core/TasksTree/Storage/DeleteStorageFileTask.cs @@ -1,4 +1,5 @@ using BUtil.Core.Events; +using BUtil.Core.Localization; using BUtil.Core.TasksTree.Core; using BUtil.Core.TasksTree.IncrementalModel; using System; @@ -14,7 +15,7 @@ public DeleteStorageFileTask( StorageSpecificServicesIoc services, TaskEvents events, string relativeFileName) : - base(services.Log, events, $"Delete storage file \"{relativeFileName}\"") + base(services.Log, events, string.Format(Resources.File_Deleting, relativeFileName)) { _services = services; _relativeFileName = relativeFileName; diff --git a/sources/BUtil.Core/TasksTree/Storage/MoveStorageFileTask.cs b/sources/BUtil.Core/TasksTree/Storage/MoveStorageFileTask.cs index 355dc1fa..d72a217d 100644 --- a/sources/BUtil.Core/TasksTree/Storage/MoveStorageFileTask.cs +++ b/sources/BUtil.Core/TasksTree/Storage/MoveStorageFileTask.cs @@ -1,4 +1,5 @@ using BUtil.Core.Events; +using BUtil.Core.Localization; using BUtil.Core.TasksTree.Core; using BUtil.Core.TasksTree.IncrementalModel; using System; @@ -16,7 +17,7 @@ public MoveStorageFileTask( TaskEvents events, string fromRelativeFileName, string toRelativeFileName) : - base(services.Log, events, $"Move storage file \"{fromRelativeFileName}\" to \"{toRelativeFileName}\"") + base(services.Log, events, string.Format(Resources.File_Moving, fromRelativeFileName, toRelativeFileName)) { _services = services; _fromRelativeFileName = fromRelativeFileName; diff --git a/sources/BUtil.Core/TasksTree/Storage/WriteStorageFilesToSourceFileTask.cs b/sources/BUtil.Core/TasksTree/Storage/WriteStorageFilesToSourceFileTask.cs index 05bb1fcf..721502c6 100644 --- a/sources/BUtil.Core/TasksTree/Storage/WriteStorageFilesToSourceFileTask.cs +++ b/sources/BUtil.Core/TasksTree/Storage/WriteStorageFilesToSourceFileTask.cs @@ -23,7 +23,7 @@ public WriteStorageFilesToSourceFileTask(ILog log, TaskEvents events, : base(log, events, Resources.Task_Restore) { _commonServicesIoc = new CommonServicesIoc(); - _storageSpecificServicesIoc = new StorageSpecificServicesIoc(log, storageSettings, _commonServicesIoc.HashService); + _storageSpecificServicesIoc = new StorageSpecificServicesIoc(log, storageSettings, _commonServicesIoc.HashService, true); Children = storageFiles .Select(x => new WriteStorageFileToSourceFileTask(_storageSpecificServicesIoc, events, sourceItem, x, destinationFolder)) diff --git a/sources/BUtil.Tests/BUtil.Tests.csproj b/sources/BUtil.Tests/BUtil.Tests.csproj index 8610af0a..6bcf159d 100644 --- a/sources/BUtil.Tests/BUtil.Tests.csproj +++ b/sources/BUtil.Tests/BUtil.Tests.csproj @@ -10,13 +10,13 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - + + diff --git a/sources/butil-ui.Desktop/Properties/launchSettings.json b/sources/butil-ui.Desktop/Properties/launchSettings.json index 211c21a5..207f3135 100644 --- a/sources/butil-ui.Desktop/Properties/launchSettings.json +++ b/sources/butil-ui.Desktop/Properties/launchSettings.json @@ -1,8 +1,7 @@ { "profiles": { "butil-ui.Desktop": { - "commandName": "Project", - "commandLineArgs": "RestoreTask \"Task=👍👍👍check bug with same content, different last write time\"" + "commandName": "Project" } } } \ No newline at end of file diff --git a/sources/butil-ui/Controls/VersionsList/VersionsListView.axaml b/sources/butil-ui/Controls/VersionsList/VersionsListView.axaml index d74a0e58..07c1b390 100644 --- a/sources/butil-ui/Controls/VersionsList/VersionsListView.axaml +++ b/sources/butil-ui/Controls/VersionsList/VersionsListView.axaml @@ -14,7 +14,7 @@ -