From 600b9f8d15c5f2615874bb06d83dbb46e8992082 Mon Sep 17 00:00:00 2001 From: Cory Knox Date: Mon, 22 Apr 2024 09:02:30 -0700 Subject: [PATCH] (#996) Create config directory if it doesn't exist If the directory for a LiteDatabase doesn't exist, LiteDatabase will not attempt to create it, and will throw an exception because it can't create the file without the parent directory. This ensures that the folder exists before trying to create the database. --- .../Startup/ChocolateyGuiModule.cs | 15 +++++++-- Source/ChocolateyGui.Common/Hacks.cs | 31 +++++++++++++++++++ .../Startup/ChocolateyGuiCliModule.cs | 15 +++++++-- 3 files changed, 55 insertions(+), 6 deletions(-) diff --git a/Source/ChocolateyGui.Common.Windows/Startup/ChocolateyGuiModule.cs b/Source/ChocolateyGui.Common.Windows/Startup/ChocolateyGuiModule.cs index ec8f01a30..1694ebd0f 100644 --- a/Source/ChocolateyGui.Common.Windows/Startup/ChocolateyGuiModule.cs +++ b/Source/ChocolateyGui.Common.Windows/Startup/ChocolateyGuiModule.cs @@ -148,13 +148,22 @@ protected override void Load(ContainerBuilder builder) var userDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.LocalAppDataPath, "data.db")};upgrade=true"); LiteDatabase globalDatabase; + var globalConfigDirectory = Path.Combine(Bootstrapper.AppDataPath, "Config"); + var globalConfigDatabaseFile = Path.Combine(globalConfigDirectory, "data.db"); + if (Hacks.IsElevated) { - globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true"); + if (!Directory.Exists(globalConfigDirectory)) + { + Directory.CreateDirectory(globalConfigDirectory); + Hacks.LockDirectory(globalConfigDirectory); + } + + globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};upgrade=true"); } else { - if (!File.Exists(Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db"))) + if (!File.Exists(globalConfigDatabaseFile)) { // Since the global configuration database file doesn't exist, we must be running in a state where an administrator user // has never run Chocolatey GUI. In this case, use null, which will mean attempts to use the global database will be ignored. @@ -163,7 +172,7 @@ protected override void Load(ContainerBuilder builder) else { // Since this is a non-administrator user, they should only have read permissions to this database - globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};readonly=true"); + globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};readonly=true"); } } diff --git a/Source/ChocolateyGui.Common/Hacks.cs b/Source/ChocolateyGui.Common/Hacks.cs index 3f147b778..1e32e621d 100644 --- a/Source/ChocolateyGui.Common/Hacks.cs +++ b/Source/ChocolateyGui.Common/Hacks.cs @@ -5,6 +5,9 @@ // // -------------------------------------------------------------------------------------------------------------------- +using System; +using System.IO; +using System.Security.AccessControl; using System.Security.Principal; namespace ChocolateyGui.Common @@ -12,5 +15,33 @@ namespace ChocolateyGui.Common public static class Hacks { public static bool IsElevated => (WindowsIdentity.GetCurrent().Owner?.IsWellKnown(WellKnownSidType.BuiltinAdministratorsSid)).GetValueOrDefault(false); + + // TODO: Replace this LockDirectory with calls to DotNetFileSystem's LockDirectory when https://github.com/chocolatey/ChocolateyGUI/issues/1046 is completed. + /// + /// Lock the given directory path to just Administrators being able to write. This method is copied from chocolatey.infrastructure.filesystem.DotNetFileSystem, and should be replaced with that call when the minimum Chocolatey.lib is bumped to 2.2.0 or greater. + /// + /// Directory path to lock down. + public static void LockDirectory(string directoryPath) + { + var permissions = Directory.GetAccessControl(directoryPath); + var rules = permissions.GetAccessRules(includeExplicit: true, includeInherited: true, targetType: typeof(NTAccount)); + + // We first need to remove all rules + foreach (FileSystemAccessRule rule in rules) + { + permissions.RemoveAccessRuleAll(rule); + } + + var bultinAdmins = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null).Translate(typeof(NTAccount)); + var localsystem = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null).Translate(typeof(NTAccount)); + var builtinUsers = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null).Translate(typeof(NTAccount)); + var inheritanceFlags = InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit; + permissions.SetAccessRule(new FileSystemAccessRule(bultinAdmins, FileSystemRights.FullControl, inheritanceFlags, PropagationFlags.None, AccessControlType.Allow)); + permissions.SetAccessRule(new FileSystemAccessRule(localsystem, FileSystemRights.FullControl, inheritanceFlags, PropagationFlags.None, AccessControlType.Allow)); + permissions.SetAccessRule(new FileSystemAccessRule(builtinUsers, FileSystemRights.ReadAndExecute, inheritanceFlags, PropagationFlags.None, AccessControlType.Allow)); + permissions.SetOwner(bultinAdmins); + permissions.SetAccessRuleProtection(isProtected: true, preserveInheritance: false); + Directory.SetAccessControl(directoryPath, permissions); + } } } \ No newline at end of file diff --git a/Source/ChocolateyGuiCli/Startup/ChocolateyGuiCliModule.cs b/Source/ChocolateyGuiCli/Startup/ChocolateyGuiCliModule.cs index 0dc97e388..215cd3ff5 100644 --- a/Source/ChocolateyGuiCli/Startup/ChocolateyGuiCliModule.cs +++ b/Source/ChocolateyGuiCli/Startup/ChocolateyGuiCliModule.cs @@ -48,13 +48,22 @@ protected override void Load(ContainerBuilder builder) var userDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.LocalAppDataPath, "data.db")};upgrade=true"); LiteDatabase globalDatabase; + var globalConfigDirectory = Path.Combine(Bootstrapper.AppDataPath, "Config"); + var globalConfigDatabaseFile = Path.Combine(globalConfigDirectory, "data.db"); + if (Hacks.IsElevated) { - globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};upgrade=true"); + if (!Directory.Exists(globalConfigDirectory)) + { + Directory.CreateDirectory(globalConfigDirectory); + Hacks.LockDirectory(globalConfigDirectory); + } + + globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};upgrade=true"); } else { - if (!File.Exists(Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db"))) + if (!File.Exists(globalConfigDatabaseFile)) { // Since the global configuration database file doesn't exist, we must be running in a state where an administrator user // has never run Chocolatey GUI. In this case, use null, which will mean attempts to use the global database will be ignored. @@ -63,7 +72,7 @@ protected override void Load(ContainerBuilder builder) else { // Since this is a non-administrator user, they should only have read permissions to this database - globalDatabase = new LiteDatabase($"filename={Path.Combine(Bootstrapper.AppDataPath, "Config", "data.db")};readonly=true"); + globalDatabase = new LiteDatabase($"filename={globalConfigDatabaseFile};readonly=true"); } }