tgstation-server  4.4.0
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 ISystemIdentity GetCurrent() => new WindowsSystemIdentity(WindowsIdentity.GetCurrent());
46 
48  public Task<ISystemIdentity> CreateSystemIdentity(User user, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
49  {
50  if (user == null)
51  throw new ArgumentNullException(nameof(user));
52 
53  if (user.SystemIdentifier == null)
54  throw new InvalidOperationException("User's SystemIdentifier must not be null!");
55 
56  PrincipalContext pc = null;
57  UserPrincipal principal = null;
58 
59  bool TryGetPrincipalFromContextType(ContextType contextType)
60  {
61  try
62  {
63  pc = new PrincipalContext(contextType);
64  cancellationToken.ThrowIfCancellationRequested();
65  principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier);
66  }
67  catch (OperationCanceledException)
68  {
69  throw;
70  }
71  catch (Exception e)
72  {
73  logger.LogWarning("Error loading user for context type {0}! Exception: {1}", contextType, e);
74  }
75  finally
76  {
77  if (principal == null)
78  {
79  pc?.Dispose();
80  cancellationToken.ThrowIfCancellationRequested();
81  }
82  }
83 
84  return principal != null;
85  }
86 
87  if (!TryGetPrincipalFromContextType(ContextType.Machine) && !TryGetPrincipalFromContextType(ContextType.Domain))
88  return null;
89  return (ISystemIdentity)new WindowsSystemIdentity(principal);
90  },
91  cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
92 
94  public Task<ISystemIdentity> CreateSystemIdentity(string username, string password, CancellationToken cancellationToken) => Task.Factory.StartNew(() =>
95  {
96  if (username == null)
97  throw new ArgumentNullException(nameof(username));
98  if (password == null)
99  throw new ArgumentNullException(nameof(password));
100 
101  var originalUsername = username;
102  GetUserAndDomainName(originalUsername, out username, out var domainName);
103 
104  var res = NativeMethods.LogonUser(username, domainName, password, 3 /*LOGON32_LOGON_NETWORK*/, 0 /*LOGON32_PROVIDER_DEFAULT*/, out var token);
105  if (!res)
106  {
107  logger.LogTrace("Invalid system identity/password combo for username {0}!", originalUsername);
108  return null;
109  }
110 
111  logger.LogTrace("Authenticated username {0} using system identity!", originalUsername);
112 
113  using (var handle = new SafeAccessTokenHandle(token)) // checked internally, windows identity always duplicates the handle when constructed with a userToken
114  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
115  }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current);
116  }
117 }
Represents a user on the current global::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 Windows 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:34