Windows identity/impersonation stuff

This commit is contained in:
Jordan Brown
2018-07-22 16:30:47 -04:00
parent f0cd3df98a
commit 669c72a05f
7 changed files with 122 additions and 4 deletions
+1 -1
View File
@@ -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);
}
}
@@ -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>