From bfbeb91633a203fa2c54d499c049a95f415319a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=81ukasz=20Domeradzki?= Date: Sun, 17 Oct 2021 00:11:04 +0200 Subject: [PATCH] Refuse to run as root without explicit ignore (#2427) * Refuse to run as root without explicit ignore * Apply feedback * Detect docker a bit better * Guard more OS parts behind ifdefs * Fix warnings * Further fixes --- ArchiSteamFarm/Core/ASF.cs | 16 +++--- ArchiSteamFarm/Core/OS.cs | 54 +++++++++++++++++++ .../Localization/Strings.Designer.cs | 12 +++++ ArchiSteamFarm/Localization/Strings.resx | 6 +++ ArchiSteamFarm/Program.cs | 25 ++++++--- ArchiSteamFarm/SharedInfo.cs | 4 +- 6 files changed, 100 insertions(+), 17 deletions(-) diff --git a/ArchiSteamFarm/Core/ASF.cs b/ArchiSteamFarm/Core/ASF.cs index f2b86148e..83cd7c934 100644 --- a/ArchiSteamFarm/Core/ASF.cs +++ b/ArchiSteamFarm/Core/ASF.cs @@ -114,7 +114,7 @@ namespace ArchiSteamFarm.Core { } if (!PluginsCore.InitPlugins()) { - await Task.Delay(10000).ConfigureAwait(false); + await Task.Delay(SharedInfo.InformationDelay).ConfigureAwait(false); } WebBrowser = new WebBrowser(ArchiLogger, GlobalConfig.WebProxy, true); @@ -170,11 +170,11 @@ namespace ArchiSteamFarm.Core { if (Program.RestartAllowed && GlobalConfig.AutoRestart) { ArchiLogger.LogGenericInfo(Strings.Restarting); - await Task.Delay(5000).ConfigureAwait(false); + await Task.Delay(SharedInfo.ShortInformationDelay).ConfigureAwait(false); await Program.Restart().ConfigureAwait(false); } else { ArchiLogger.LogGenericInfo(Strings.Exiting); - await Task.Delay(5000).ConfigureAwait(false); + await Task.Delay(SharedInfo.ShortInformationDelay).ConfigureAwait(false); await Program.Exit().ConfigureAwait(false); } } @@ -241,7 +241,7 @@ namespace ArchiSteamFarm.Core { if (!updateOverride && (GlobalConfig.UpdatePeriod == 0)) { ArchiLogger.LogGenericInfo(Strings.UpdateNewVersionAvailable); - await Task.Delay(5000).ConfigureAwait(false); + await Task.Delay(SharedInfo.ShortInformationDelay).ConfigureAwait(false); return null; } @@ -314,6 +314,7 @@ namespace ArchiSteamFarm.Core { return null; } +#if TARGET_GENERIC || !TARGET_WINDOWS if (OperatingSystem.IsFreeBSD() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { string executable = Path.Combine(SharedInfo.HomeDirectory, SharedInfo.AssemblyName); @@ -321,6 +322,7 @@ namespace ArchiSteamFarm.Core { OS.UnixSetFileAccess(executable, OS.EUnixPermission.Combined755); } } +#endif ArchiLogger.LogGenericInfo(Strings.UpdateFinished); @@ -840,8 +842,6 @@ namespace ArchiSteamFarm.Core { } catch (Exception e) { ArchiLogger.LogGenericWarningException(e); ArchiLogger.LogGenericWarning(Strings.BotSteamDirectoryInitializationFailed); - - await Task.Delay(5000).ConfigureAwait(false); } } @@ -862,7 +862,7 @@ namespace ArchiSteamFarm.Core { return; case > MaximumRecommendedBotsCount: ArchiLogger.LogGenericWarning(string.Format(CultureInfo.CurrentCulture, Strings.WarningExcessiveBotsCount, MaximumRecommendedBotsCount)); - await Task.Delay(10000).ConfigureAwait(false); + await Task.Delay(SharedInfo.InformationDelay).ConfigureAwait(false); break; } @@ -901,7 +901,7 @@ namespace ArchiSteamFarm.Core { if (SharedInfo.Version >= newVersion) { if (SharedInfo.Version > newVersion) { ArchiLogger.LogGenericWarning(Strings.WarningPreReleaseVersion); - await Task.Delay(15 * 1000).ConfigureAwait(false); + await Task.Delay(SharedInfo.InformationDelay).ConfigureAwait(false); } return; diff --git a/ArchiSteamFarm/Core/OS.cs b/ArchiSteamFarm/Core/OS.cs index cac76995c..4588fa4c5 100644 --- a/ArchiSteamFarm/Core/OS.cs +++ b/ArchiSteamFarm/Core/OS.cs @@ -31,6 +31,7 @@ using System.Globalization; using System.IO; using System.Runtime.InteropServices; using System.Security.Cryptography; +using System.Security.Principal; using System.Text; using System.Text.RegularExpressions; using System.Threading; @@ -94,6 +95,7 @@ namespace ArchiSteamFarm.Core { private static string? BackingVersion; private static Mutex? SingleInstance; +#if TARGET_GENERIC || TARGET_WINDOWS internal static void CoreInit(bool systemRequired) { if (OperatingSystem.IsWindows()) { if (systemRequired) { @@ -114,6 +116,9 @@ namespace ArchiSteamFarm.Core { } } } +#else + internal static void CoreInit(bool _) { } +#endif internal static string GetOsResourceName(string objectName) { if (string.IsNullOrEmpty(objectName)) { @@ -142,6 +147,32 @@ namespace ArchiSteamFarm.Core { } } + internal static bool IsRunningAsRoot() { +#if TARGET_GENERIC || TARGET_WINDOWS + if (OperatingSystem.IsWindows()) { + using WindowsIdentity identity = WindowsIdentity.GetCurrent(); + + return new WindowsPrincipal(identity).IsInRole(WindowsBuiltInRole.Administrator); + } +#endif + +#if TARGET_GENERIC || !TARGET_WINDOWS + if (OperatingSystem.IsFreeBSD() || OperatingSystem.IsLinux() || OperatingSystem.IsMacOS()) { + return NativeMethods.GetEUID() == 0; + } +#endif + + // We can't determine whether user is running as root or not, so fallback to that not happening + return false; + } + + internal static bool IsRunningInDocker() => +#if ASF_VARIANT_DOCKER + true; +#else + Environment.GetEnvironmentVariable("DOTNET_RUNNING_IN_CONTAINER") == "true"; +#endif + internal static async Task RegisterProcess() { if (SingleInstance != null) { return false; @@ -182,6 +213,7 @@ namespace ArchiSteamFarm.Core { return true; } +#if TARGET_GENERIC || !TARGET_WINDOWS [SupportedOSPlatform("FreeBSD")] [SupportedOSPlatform("Linux")] [SupportedOSPlatform("MacOS")] @@ -205,6 +237,7 @@ namespace ArchiSteamFarm.Core { ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, Marshal.GetLastWin32Error())); } } +#endif internal static void UnregisterProcess() { if (SingleInstance == null) { @@ -248,6 +281,7 @@ namespace ArchiSteamFarm.Core { #endif } +#if TARGET_GENERIC || TARGET_WINDOWS [SupportedOSPlatform("Windows")] private static void WindowsDisableQuickEditMode() { if (!OperatingSystem.IsWindows()) { @@ -285,7 +319,9 @@ namespace ArchiSteamFarm.Core { ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningFailedWithError, result)); } } +#endif +#if TARGET_GENERIC || !TARGET_WINDOWS [Flags] [SupportedOSPlatform("FreeBSD")] [SupportedOSPlatform("Linux")] @@ -303,8 +339,10 @@ namespace ArchiSteamFarm.Core { Combined755 = UserRead | UserWrite | UserExecute | GroupRead | GroupExecute | OtherRead | OtherExecute, Combined777 = UserRead | UserWrite | UserExecute | GroupRead | GroupWrite | GroupExecute | OtherRead | OtherWrite | OtherExecute } +#endif private static class NativeMethods { +#if TARGET_GENERIC || TARGET_WINDOWS [SupportedOSPlatform("Windows")] internal const EExecutionState AwakeExecutionState = EExecutionState.SystemRequired | EExecutionState.AwayModeRequired | EExecutionState.Continuous; @@ -313,7 +351,9 @@ namespace ArchiSteamFarm.Core { [SupportedOSPlatform("Windows")] internal const sbyte StandardInputHandle = -10; +#endif +#if TARGET_GENERIC || !TARGET_WINDOWS #pragma warning disable CA2101 // False positive, we can't use unicode charset on Unix, and it uses UTF-8 by default anyway [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [DllImport("libc", EntryPoint = "chmod", SetLastError = true)] @@ -322,12 +362,25 @@ namespace ArchiSteamFarm.Core { [SupportedOSPlatform("MacOS")] internal static extern int Chmod(string path, int mode); #pragma warning restore CA2101 // False positive, we can't use unicode charset on Unix, and it uses UTF-8 by default anyway +#endif +#if TARGET_GENERIC || TARGET_WINDOWS [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [DllImport("kernel32.dll")] [SupportedOSPlatform("Windows")] internal static extern bool GetConsoleMode(IntPtr hConsoleHandle, out uint lpMode); +#endif +#if TARGET_GENERIC || !TARGET_WINDOWS + [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] + [DllImport("libc", EntryPoint = "geteuid", SetLastError = true)] + [SupportedOSPlatform("FreeBSD")] + [SupportedOSPlatform("Linux")] + [SupportedOSPlatform("MacOS")] + internal static extern uint GetEUID(); +#endif + +#if TARGET_GENERIC || TARGET_WINDOWS [DefaultDllImportSearchPaths(DllImportSearchPath.System32)] [DllImport("kernel32.dll")] [SupportedOSPlatform("Windows")] @@ -351,6 +404,7 @@ namespace ArchiSteamFarm.Core { AwayModeRequired = 0x00000040, Continuous = 0x80000000 } +#endif } } } diff --git a/ArchiSteamFarm/Localization/Strings.Designer.cs b/ArchiSteamFarm/Localization/Strings.Designer.cs index 0ff55295a..b774ae015 100644 --- a/ArchiSteamFarm/Localization/Strings.Designer.cs +++ b/ArchiSteamFarm/Localization/Strings.Designer.cs @@ -1166,5 +1166,17 @@ namespace ArchiSteamFarm.Localization { return ResourceManager.GetString("WarningDefaultCryptKeyUsedForEncryption", resourceCulture); } } + + public static string WarningRunningAsRoot { + get { + return ResourceManager.GetString("WarningRunningAsRoot", resourceCulture); + } + } + + public static string WarningRunningInUnsupportedEnvironment { + get { + return ResourceManager.GetString("WarningRunningInUnsupportedEnvironment", resourceCulture); + } + } } } diff --git a/ArchiSteamFarm/Localization/Strings.resx b/ArchiSteamFarm/Localization/Strings.resx index c20137927..25adcd2fb 100644 --- a/ArchiSteamFarm/Localization/Strings.resx +++ b/ArchiSteamFarm/Localization/Strings.resx @@ -723,4 +723,10 @@ Process uptime: {1} You're using {0} setting of {1} property, but you didn't provide a custom --cryptkey. This entirely defeats the protection, as ASF is forced to use its own (known) key. You should provide a custom --cryptkey for making use of the security benefit offered by this setting. {0} will be replaced by the name of a particular setting (e.g. "AES"), {1} will be replaced by the name of the property (e.g. "SteamPassword") + + You're attempting to run ASF as the administrator (root). This causes a significant security risk to your machine, and as ASF does not require root access for its operation, we do not support this scenario. Supply --ignore-unsupported-environment argument if you really know what you're doing. + + + You're running ASF in unsupported environment, supplying --ignore-unsupported-environment argument. Please note that we do not offer any kind of support for this scenario and you're doing it entirely at your own risk. You've been warned. + diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs index 16819300b..511c37667 100644 --- a/ArchiSteamFarm/Program.cs +++ b/ArchiSteamFarm/Program.cs @@ -193,7 +193,7 @@ namespace ArchiSteamFarm { if (!uniqueInstance) { ASF.ArchiLogger.LogGenericError(Strings.ErrorSingleInstanceRequired); - await Task.Delay(10000).ConfigureAwait(false); + await Task.Delay(SharedInfo.InformationDelay).ConfigureAwait(false); return false; } @@ -210,10 +210,19 @@ namespace ArchiSteamFarm { ASF.ArchiLogger.LogGenericInfo(copyright!); } - if (!IgnoreUnsupportedEnvironment) { + if (IgnoreUnsupportedEnvironment) { + ASF.ArchiLogger.LogGenericWarning(Strings.WarningRunningInUnsupportedEnvironment); + } else { if (!OS.VerifyEnvironment()) { ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.WarningUnsupportedEnvironment, SharedInfo.BuildInfo.Variant, OS.Version)); - await Task.Delay(10000).ConfigureAwait(false); + await Task.Delay(SharedInfo.InformationDelay).ConfigureAwait(false); + + return false; + } + + if (!OS.IsRunningInDocker() && OS.IsRunningAsRoot()) { + ASF.ArchiLogger.LogGenericError(Strings.WarningRunningAsRoot); + await Task.Delay(SharedInfo.InformationDelay).ConfigureAwait(false); return false; } @@ -221,7 +230,7 @@ namespace ArchiSteamFarm { if (!Directory.Exists(SharedInfo.ConfigDirectory)) { ASF.ArchiLogger.LogGenericError(Strings.ErrorConfigDirectoryNotFound); - await Task.Delay(10000).ConfigureAwait(false); + await Task.Delay(SharedInfo.InformationDelay).ConfigureAwait(false); return false; } @@ -245,7 +254,7 @@ namespace ArchiSteamFarm { if (globalConfig == null) { ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorGlobalConfigNotLoaded, globalConfigFile)); - await Task.Delay(5000).ConfigureAwait(false); + await Task.Delay(SharedInfo.ShortInformationDelay).ConfigureAwait(false); return false; } @@ -348,16 +357,16 @@ namespace ArchiSteamFarm { if (!File.Exists(globalDatabaseFile)) { ASF.ArchiLogger.LogGenericInfo(Strings.Welcome); - await Task.Delay(10000).ConfigureAwait(false); + await Task.Delay(SharedInfo.InformationDelay).ConfigureAwait(false); ASF.ArchiLogger.LogGenericWarning(Strings.WarningPrivacyPolicy); - await Task.Delay(5000).ConfigureAwait(false); + await Task.Delay(SharedInfo.ShortInformationDelay).ConfigureAwait(false); } GlobalDatabase? globalDatabase = await GlobalDatabase.CreateOrLoad(globalDatabaseFile).ConfigureAwait(false); if (globalDatabase == null) { ASF.ArchiLogger.LogGenericError(string.Format(CultureInfo.CurrentCulture, Strings.ErrorDatabaseInvalid, globalDatabaseFile)); - await Task.Delay(5000).ConfigureAwait(false); + await Task.Delay(SharedInfo.ShortInformationDelay).ConfigureAwait(false); return false; } diff --git a/ArchiSteamFarm/SharedInfo.cs b/ArchiSteamFarm/SharedInfo.cs index a0edb660e..7253184f7 100644 --- a/ArchiSteamFarm/SharedInfo.cs +++ b/ArchiSteamFarm/SharedInfo.cs @@ -47,6 +47,7 @@ namespace ArchiSteamFarm { internal const string GithubRepo = "JustArchiNET/" + AssemblyName; internal const string GlobalConfigFileName = ASF + JsonConfigExtension; internal const string GlobalDatabaseFileName = ASF + DatabaseExtension; + internal const ushort InformationDelay = 10000; internal const string IPCConfigExtension = ".config"; internal const string IPCConfigFile = nameof(IPC) + IPCConfigExtension; internal const string JsonConfigExtension = ".json"; @@ -54,12 +55,13 @@ namespace ArchiSteamFarm { internal const string KeysUnusedExtension = ".unused"; internal const string KeysUsedExtension = ".used"; internal const string LicenseName = "Apache 2.0"; - internal const string LicenseURL = "http://www.apache.org/licenses/LICENSE-2.0"; + internal const string LicenseURL = "https://www.apache.org/licenses/LICENSE-2.0"; internal const string LogFile = "log.txt"; internal const string MobileAuthenticatorExtension = ".maFile"; internal const string PluginsDirectory = "plugins"; internal const string ProjectURL = "https://github.com/" + GithubRepo; internal const string SentryHashExtension = ".bin"; + internal const ushort ShortInformationDelay = InformationDelay / 2; internal const string StatisticsServer = "asf.justarchi.net"; internal const string UlongCompatibilityStringPrefix = "s_"; internal const string UpdateDirectory = "_old";