using Microsoft.Win32.SafeHandles; using System; using System.DirectoryServices.AccountManagement; 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!"); PrincipalContext pc = null; UserPrincipal principal = null; //machine logon first cause it's faster pc = new PrincipalContext(ContextType.Machine); principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier); if (principal == null) { pc.Dispose(); //try domain now try { pc = new PrincipalContext(ContextType.Domain); principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier); } catch (PrincipalServerDownException) { } if (principal == null) { pc?.Dispose(); return null; } } return (ISystemIdentity)new WindowsSystemIdentity(principal); }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); /// public Task CreateSystemIdentity(string username, string password, CancellationToken cancellationToken) => Task.Factory.StartNew(() => { if (username == null) throw new ArgumentNullException(nameof(username)); if (password == null) throw new ArgumentNullException(nameof(password)); 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) return null; 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); } }