tgstation-server
The /tg/station 13 server suite
WindowsSystemIdentityFactory.cs
Go to the documentation of this file.
1 using Microsoft.Extensions.Logging;
2 using Microsoft.Win32.SafeHandles;
3 using System;
4 using System.DirectoryServices.AccountManagement;
5 using System.Security.Principal;
6 using System.Threading;
7 using System.Threading.Tasks;
9 
10 namespace Tgstation.Server.Host.Security
11 {
16  {
20  readonly ILogger<WindowsSystemIdentityFactory> logger;
21 
28  static void GetUserAndDomainName(string input, out string username, out string domainName)
29  {
30  var splits = input.Split('\\');
31  username = splits.Length > 1 ? splits[1] : splits[0];
32  domainName = splits.Length > 1 ? splits[0] : null;
33  }
34 
39  public WindowsSystemIdentityFactory(ILogger<WindowsSystemIdentityFactory> logger)
40  {
41  this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
42  }
43 
45  public Task<ISystemIdentity> CreateSystemIdentity(User user, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
46  {
47  if (user == null)
48  throw new ArgumentNullException(nameof(user));
49 
50  if (user.SystemIdentifier == null)
51  throw new InvalidOperationException("User's SystemIdentifier must not be null!");
52 
53  PrincipalContext pc = null;
54  UserPrincipal principal = null;
55 
56  bool TryGetPrincipalFromContextType(ContextType contextType)
57  {
58  try
59  {
60  pc = new PrincipalContext(contextType);
61  cancellationToken.ThrowIfCancellationRequested();
62  principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier);
63  }
64  catch (OperationCanceledException)
65  {
66  throw;
67  }
68  catch (Exception e)
69  {
70  logger.LogWarning("Error loading user for context type {0}! Exception: {1}", contextType, e);
71  }
72  finally
73  {
74  if (principal == null)
75  {
76  pc?.Dispose();
77  cancellationToken.ThrowIfCancellationRequested();
78  }
79  }
80  return principal != null;
81  };
82 
83  if (!TryGetPrincipalFromContextType(ContextType.Machine) && !TryGetPrincipalFromContextType(ContextType.Domain))
84  return null;
85  return (ISystemIdentity)new WindowsSystemIdentity(principal);
86  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
87 
89  public Task<ISystemIdentity> CreateSystemIdentity(string username, string password, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
90  {
91  if (username == null)
92  throw new ArgumentNullException(nameof(username));
93  if (password == null)
94  throw new ArgumentNullException(nameof(password));
95 
96  var originalUsername = username;
97  GetUserAndDomainName(originalUsername, out username, out var domainName);
98 
99  var res = NativeMethods.LogonUser(username, domainName, password, 3 /*LOGON32_LOGON_NETWORK*/, 0 /*LOGON32_PROVIDER_DEFAULT*/, out var token);
100  if (!res)
101  {
102  logger.LogTrace("Failed to log in username {0}!", originalUsername);
103  return null;
104  }
105 
106  logger.LogTrace("Successfully logged in username {0}!", originalUsername);
107 
108  using (var handle = new SafeAccessTokenHandle(token)) //checked internally, windows identity always duplicates the handle when constructed with a userToken
109  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
110  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
111  }
112 }
Represents a user on the current System.Runtime.InteropServices.OSPlatform
readonly ILogger< WindowsSystemIdentityFactory > logger
The ILogger for the WindowsSystemIdentityFactory
WindowsSystemIdentityFactory(ILogger< WindowsSystemIdentityFactory > logger)
Construct a WindowsSystemIdentityFactory
ISystemIdentityFactory for windows systems. Uses long running tasks due to potential networked domain...
static bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken)
See https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx ...
Native methods used by the code
static void GetUserAndDomainName(string input, out string username, out string domainName)
Extract the username and domain name from a string in the format "username\\domainname" ...
string SystemIdentifier
The SID/UID of the User on Windows/POSIX respectively
Definition: User.cs:32