mirror of
https://github.com/tgstation/tgstation-server.git
synced 2026-08-27 07:04:57 +01:00
Windows identity/impersonation stuff
This commit is contained in:
@@ -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";
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace Tgstation.Server.Host.Core
|
||||
services.AddSingleton<IDatabaseSeeder, DatabaseSeeder>();
|
||||
services.AddSingleton<IPasswordHasher<Models.User>, PasswordHasher<Models.User>>();
|
||||
services.AddSingleton<ITokenFactory, TokenFactory>();
|
||||
services.AddSingleton<ISystemIdentityFactory, SystemIdentityFactory>();
|
||||
services.AddSingleton<ISystemIdentityFactory, WindowsSystemIdentityFactory>();
|
||||
|
||||
services.AddSingleton<IExecutor, Executor>();
|
||||
services.AddSingleton<ICommandFactory, CommandFactory>();
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Tgstation.Server.Host
|
||||
{
|
||||
/// <summary>
|
||||
/// Native methods used by the code
|
||||
/// </summary>
|
||||
static class NativeMethods
|
||||
{
|
||||
/// <summary>
|
||||
/// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx
|
||||
/// </summary>
|
||||
[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);
|
||||
}
|
||||
}
|
||||
+4
-2
@@ -5,8 +5,10 @@ using Tgstation.Server.Host.Models;
|
||||
|
||||
namespace Tgstation.Server.Host.Security
|
||||
{
|
||||
/// <inheritdoc />
|
||||
sealed class SystemIdentityFactory : ISystemIdentityFactory
|
||||
/// <summary>
|
||||
/// <see cref="ISystemIdentityFactory"/> for posix systems
|
||||
/// </summary>
|
||||
sealed class PosixSystemIdentityFactory : ISystemIdentityFactory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task<ISystemIdentity> CreateSystemIdentity(User user, CancellationToken cancellationToken)
|
||||
@@ -0,0 +1,47 @@
|
||||
using System;
|
||||
using System.Security.Principal;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Tgstation.Server.Host.Security
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="ISystemIdentity"/> for windows systems
|
||||
/// </summary>
|
||||
sealed class WindowsSystemIdentity : ISystemIdentity
|
||||
{
|
||||
/// <summary>
|
||||
/// The <see cref="WindowsIdentity"/> for the <see cref="WindowsSystemIdentity"/>
|
||||
/// </summary>
|
||||
readonly WindowsIdentity identity;
|
||||
|
||||
/// <summary>
|
||||
/// Construct a <see cref="WindowsSystemIdentity"/>
|
||||
/// </summary>
|
||||
/// <param name="identity">The value of <see cref="identity"/></param>
|
||||
public WindowsSystemIdentity(WindowsIdentity identity)
|
||||
{
|
||||
this.identity = identity ?? throw new ArgumentNullException(nameof(identity));
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => identity.Dispose();
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Uid => identity.User.ToString();
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Username => identity.Name;
|
||||
|
||||
/// <inheritdoc />
|
||||
public ISystemIdentity Clone() => new WindowsSystemIdentity((WindowsIdentity)identity.Clone());
|
||||
|
||||
/// <inheritdoc />
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
{
|
||||
/// <summary>
|
||||
/// <see cref="ISystemIdentityFactory"/> for windows systems. Uses long running tasks due to potential networked domains
|
||||
/// </summary>
|
||||
sealed class WindowsSystemIdentityFactory : ISystemIdentityFactory
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public Task<ISystemIdentity> 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);
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ISystemIdentity> 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);
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,7 @@
|
||||
<PackageReference Include="MySql.Data.EntityFrameworkCore" Version="8.0.11" />
|
||||
<PackageReference Include="Octokit" Version="0.30.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.2.4" />
|
||||
<PackageReference Include="System.Security.Principal.Windows" Version="4.5.0" />
|
||||
<PackageReference Include="Z.EntityFramework.Plus.EFCore" Version="1.8.4" />
|
||||
<PackageReference Include="ZNetCS.AspNetCore.Logging.EntityFrameworkCore" Version="2.0.1" />
|
||||
</ItemGroup>
|
||||
|
||||
Reference in New Issue
Block a user