diff --git a/src/Tgstation.Server.Host.Service/Program.cs b/src/Tgstation.Server.Host.Service/Program.cs index 742b7e7ac6..96932140c2 100644 --- a/src/Tgstation.Server.Host.Service/Program.cs +++ b/src/Tgstation.Server.Host.Service/Program.cs @@ -75,7 +75,7 @@ namespace Tgstation.Server.Host.Service using (var processInstaller = new ServiceProcessInstaller()) using (var installer = new ServiceInstaller()) { - processInstaller.Account = ServiceAccount.NetworkService; + processInstaller.Account = ServiceAccount.LocalSystem; installer.Context = new InstallContext("tgs-4-install.log", new string[] { String.Format(CultureInfo.InvariantCulture, "/assemblypath={0}", Assembly.GetEntryAssembly().Location) }); installer.Description = "/tg/station 13 server v4 running as a windows service"; diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs index 351cb649ec..5b9648bd91 100644 --- a/src/Tgstation.Server.Host/Core/Application.cs +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -146,7 +146,7 @@ namespace Tgstation.Server.Host.Core services.AddSingleton(); services.AddSingleton, PasswordHasher>(); services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); services.AddSingleton(); diff --git a/src/Tgstation.Server.Host/NativeMethods.cs b/src/Tgstation.Server.Host/NativeMethods.cs new file mode 100644 index 0000000000..8a4038dc53 --- /dev/null +++ b/src/Tgstation.Server.Host/NativeMethods.cs @@ -0,0 +1,17 @@ +using System; +using System.Runtime.InteropServices; + +namespace Tgstation.Server.Host +{ + /// + /// Native methods used by the code + /// + static class NativeMethods + { + /// + /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx + /// + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken); + } +} diff --git a/src/Tgstation.Server.Host/Security/SystemIdentityFactory.cs b/src/Tgstation.Server.Host/Security/PosixSystemIdentityFactory.cs similarity index 79% rename from src/Tgstation.Server.Host/Security/SystemIdentityFactory.cs rename to src/Tgstation.Server.Host/Security/PosixSystemIdentityFactory.cs index 4f384ff4b4..659fe36260 100644 --- a/src/Tgstation.Server.Host/Security/SystemIdentityFactory.cs +++ b/src/Tgstation.Server.Host/Security/PosixSystemIdentityFactory.cs @@ -5,8 +5,10 @@ using Tgstation.Server.Host.Models; namespace Tgstation.Server.Host.Security { - /// - sealed class SystemIdentityFactory : ISystemIdentityFactory + /// + /// for posix systems + /// + sealed class PosixSystemIdentityFactory : ISystemIdentityFactory { /// public Task CreateSystemIdentity(User user, CancellationToken cancellationToken) diff --git a/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs b/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs new file mode 100644 index 0000000000..626d699ac7 --- /dev/null +++ b/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs @@ -0,0 +1,47 @@ +using System; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Security +{ + /// + /// for windows systems + /// + sealed class WindowsSystemIdentity : ISystemIdentity + { + /// + /// The for the + /// + readonly WindowsIdentity identity; + + /// + /// Construct a + /// + /// The value of + public WindowsSystemIdentity(WindowsIdentity identity) + { + this.identity = identity ?? throw new ArgumentNullException(nameof(identity)); + } + + /// + public void Dispose() => identity.Dispose(); + + /// + public string Uid => identity.User.ToString(); + + /// + public string Username => identity.Name; + + /// + public ISystemIdentity Clone() => new WindowsSystemIdentity((WindowsIdentity)identity.Clone()); + + /// + public Task RunImpersonated(Action action, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + WindowsIdentity.RunImpersonated(identity.AccessToken, action); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs new file mode 100644 index 0000000000..286d2daaec --- /dev/null +++ b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs @@ -0,0 +1,51 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.ComponentModel; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + /// for windows systems. Uses long running tasks due to potential networked domains + /// + sealed class WindowsSystemIdentityFactory : ISystemIdentityFactory + { + /// + public Task CreateSystemIdentity(User user, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + if (user == null) + throw new ArgumentNullException(nameof(user)); + + if (user.SystemIdentifier == null) + throw new InvalidOperationException("User's SystemIdentifier must not be null!"); + + //System identity at this point will always be in the form DOMAIN\\USER or USER + var splits = user.SystemIdentifier.Split('\\'); + string identity; + if (splits.Length > 1) + identity = String.Format(CultureInfo.InvariantCulture, "{0}@{1}", splits[0], splits[1]); + else + identity = user.SystemIdentifier; + + return (ISystemIdentity)new WindowsSystemIdentity(new WindowsIdentity(identity)); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public Task CreateSystemIdentity(string username, string password, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + var splits = username.Split('\\'); + + var res = NativeMethods.LogonUser(splits.Length > 1 ? splits[1] : splits[0], splits.Length > 1 ? splits[0] : null, password, 3 /*LOGON32_LOGON_NETWORK*/, 0 /*LOGON32_PROVIDER_DEFAULT*/, out var token); + if (!res) + throw new Win32Exception(Marshal.GetLastWin32Error()); + + using (var handle = new SafeAccessTokenHandle(token)) //checked internally, windows identity always duplicates the handle when constructed with a userToken + return (ISystemIdentity)new WindowsSystemIdentity(new WindowsIdentity(handle.DangerousGetHandle())); //https://github.com/dotnet/corefx/blob/6ed61acebe3214fcf79b4274f2bb9b55c0604a4d/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs#L271 + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + } +} diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj index aa1f136bcd..a396bd0c66 100644 --- a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -48,6 +48,7 @@ +