using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Security.Cryptography; using System.Text; using System.Windows.Forms; using System.Security.Principal; // For NTFS permissions using System.Security.AccessControl; // For NTFS permissions using System.Drawing.Text; //using MySqlConnector; //using System.Data; //using System.Diagnostics; //using static System.Windows.Forms.VisualStyles.VisualStyleElement.TaskbarClock; //using Backup_All_Databases.Properties; //using System.Drawing; namespace Backup_All_Databases { internal static class Program { internal static PrivateFontCollection myFontCollection = new PrivateFontCollection(); internal static string ConfigFilePath => Path.Combine(Application.StartupPath, "config.dat"); internal static string KeyFilePath => Path.Combine(Application.StartupPath, "kdac.bin"); internal static string saltFilePath => Path.Combine(Application.StartupPath, "salt.bin"); [STAThread] static void Main(string[] args) { if (args != null && args.Length > 0) { foreach (string arg in args) { if (arg.ToUpper() == "/SILENT") { try { var config = ReadConfigFile(false); if (config != null) { Backup backup = new Backup(); backup.Run(config); config = null; } GC.Collect(); } catch { } return; } } } Application.EnableVisualStyles(); Application.SetCompatibleTextRenderingDefault(false); myFontCollection.AddFontFile(Path.Combine(Application.StartupPath, "CascadiaCode.ttf")); Application.Run(new Form1()); } /// /// Prompts the user for a password via a dialog. /// private static string GetUserInputPwd() { using (FormPwd f1 = new FormPwd()) { if (f1.ShowDialog() != DialogResult.OK) return null; string pwd = f1.pwd?.Trim(); if (string.IsNullOrEmpty(pwd)) { MessageBox.Show("Password cannot be empty.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); return null; } return pwd; } } /// /// Reads and decrypts the configuration file. /// public static Config ReadConfigFile(bool p) { if (!File.Exists(ConfigFilePath)) return null; byte[] key; if (File.Exists(KeyFilePath) && !p) { byte[] protectedKey = File.ReadAllBytes(KeyFilePath); try { key = EncryptionHelper.UnprotectKey(protectedKey); } catch (CryptographicException) { throw new Exception("Unable to access stored key."); } } else { string password = GetUserInputPwd(); if (string.IsNullOrEmpty(password)) return null; key = EncryptionHelper.GenerateKey(password); } try { byte[] fileData = File.ReadAllBytes(ConfigFilePath); byte[] decryptedData = EncryptionHelper.Decrypt(fileData, key); string configString = Encoding.UTF8.GetString(decryptedData); string[] parts = configString.Split(new[] { "|||" }, StringSplitOptions.None); if (parts.Length != 8) { throw new Exception("Invalid, outdated, incompatible configuration file"); } int.TryParse(parts[2], out int maxBackupCopies); Config config = new Config(); config.ConnectionString = parts[0]; config.BackupPath = parts[1]; config.MaxBackupCopies = maxBackupCopies; config.BackupAllDatabases = bool.Parse(parts[3]); config.IncludeList = parts[4].Split(',').ToList(); config.ExcludeList = parts[5].Split(',').ToList(); config.StartHour = Convert.ToInt32(parts[6]); config.StartMinute = Convert.ToInt32(parts[7]); config.Key = key; Array.Clear(decryptedData, 0, decryptedData.Length); return config; } catch (CryptographicException ex) { throw new Exception("Decryption Error: Incorrect password or corrupted file."); } catch (Exception ex) { throw new Exception($"Failed to read config: {ex.Message}"); } } /// /// Writes the configuration to an encrypted file. /// public static void WriteConfigFile(Config newConfig) { if (newConfig == null) { throw new Exception("Configuration cannot be null."); } byte[] key = null; string password = null; try { if (File.Exists(ConfigFilePath)) { Config existingConfig = ReadConfigFile(true); if (existingConfig == null) { throw new Exception("Cannot write config: unable to read existing config."); } key = existingConfig.Key; } else { password = GetUserInputPwd(); if (string.IsNullOrEmpty(password)) return; try { key = EncryptionHelper.GenerateKey(password); } catch (Exception ex) { throw new Exception($"Failed to generate encryption key: {ex.Message}"); } try { byte[] protectedKey = EncryptionHelper.ProtectKey(key); File.WriteAllBytes(KeyFilePath, protectedKey); SetFilePermissions(KeyFilePath); } catch (Exception ex) { throw new Exception($"Failed to store protected key: {ex.Message}"); } } try { string configString = string.Join("|||", newConfig.ConnectionString ?? "", newConfig.BackupPath ?? "", newConfig.MaxBackupCopies.ToString(), newConfig.BackupAllDatabases.ToString(), string.Join(",", newConfig.IncludeList ?? new List()), string.Join(",", newConfig.ExcludeList ?? new List()), newConfig.StartHour.ToString(), newConfig.StartMinute.ToString()); byte[] data = Encoding.UTF8.GetBytes(configString); byte[] encryptedData = EncryptionHelper.Encrypt(data, key); File.WriteAllBytes(ConfigFilePath, encryptedData); SetFilePermissions(ConfigFilePath); // Set NTFS permissions for config file Array.Clear(data, 0, data.Length); Array.Clear(encryptedData, 0, encryptedData.Length); } catch (Exception ex) { throw new Exception($"Failed to write config file: {ex.Message}"); } } finally { if (key != null) Array.Clear(key, 0, key.Length); if (password != null) { Array.Clear(Encoding.UTF8.GetBytes(password), 0, password.Length); password = null; } } } /// /// Sets NTFS permissions on a file to allow access only to SYSTEM and the current user. /// public static void SetFilePermissions(string filePath) { try { FileSecurity fileSecurity = File.GetAccessControl(filePath); fileSecurity.SetAccessRuleProtection(true, false); // Disable inheritance // Remove all existing access rules foreach (FileSystemAccessRule rule in fileSecurity.GetAccessRules(true, true, typeof(NTAccount))) { fileSecurity.RemoveAccessRule(rule); } // Add access for SYSTEM fileSecurity.AddAccessRule(new FileSystemAccessRule( new NTAccount("NT AUTHORITY\\SYSTEM"), FileSystemRights.FullControl, AccessControlType.Allow)); // Add access for the current user string currentUser = WindowsIdentity.GetCurrent().Name; fileSecurity.AddAccessRule(new FileSystemAccessRule( new NTAccount(currentUser), FileSystemRights.FullControl, AccessControlType.Allow)); File.SetAccessControl(filePath, fileSecurity); } catch (Exception ex) { MessageBox.Show($"Failed to set permissions on {filePath}: {ex.Message}", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } } }