return buffer;
}
+ public void CompleteRequestProcessing()
+ {
+ _contentLength = 0;
+ _bodyRead = false;
+ }
+
public override async Task SendResponseAsync(HttpStatusCode statusCode = HttpStatusCode.OK, IList<HttpHeaderData> headers = null, string content = "", bool isFinal = true, int requestId = 0)
{
MemoryStream headerBytes = new MemoryStream();
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.ComponentModel;
+using System.Linq;
+using System.Net;
+using System.Runtime.InteropServices;
+using System.Security.Cryptography;
+using System.Security.Principal;
+using System.Threading.Tasks;
+using Microsoft.Win32.SafeHandles;
+
+namespace System
+{
+ public class WindowsIdentityFixture : IDisposable
+ {
+ public WindowsTestAccount TestAccount { get; private set; }
+
+ public WindowsIdentityFixture()
+ {
+ TestAccount = new WindowsTestAccount("CorFxTstWiIde01kiu");
+ }
+
+ public void Dispose()
+ {
+ TestAccount.Dispose();
+ }
+ }
+
+ public sealed class WindowsTestAccount : IDisposable
+ {
+ private readonly string _userName;
+ private SafeAccessTokenHandle _accountTokenHandle;
+ public SafeAccessTokenHandle AccountTokenHandle => _accountTokenHandle;
+ public string AccountName { get; private set; }
+
+ public WindowsTestAccount(string userName)
+ {
+ _userName = userName;
+ CreateUser();
+ }
+
+ private void CreateUser()
+ {
+ string testAccountPassword;
+ using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
+ {
+ byte[] randomBytes = new byte[33];
+ rng.GetBytes(randomBytes);
+
+ // Add special chars to ensure it satisfies password requirements.
+ testAccountPassword = Convert.ToBase64String(randomBytes) + "_-As@!%*(1)4#2";
+
+ USER_INFO_1 userInfo = new USER_INFO_1
+ {
+ usri1_name = _userName,
+ usri1_password = testAccountPassword,
+ usri1_priv = 1
+ };
+
+ // Create user and remove/create if already exists
+ uint result = NetUserAdd(null, 1, ref userInfo, out uint param_err);
+
+ // error codes https://docs.microsoft.com/en-us/windows/desktop/netmgmt/network-management-error-codes
+ // 0 == NERR_Success
+ if (result == 2224) // NERR_UserExists
+ {
+ result = NetUserDel(null, userInfo.usri1_name);
+ if (result != 0)
+ {
+ throw new Win32Exception((int)result);
+ }
+ result = NetUserAdd(null, 1, ref userInfo, out param_err);
+ if (result != 0)
+ {
+ throw new Win32Exception((int)result);
+ }
+ }
+
+ const int LOGON32_PROVIDER_DEFAULT = 0;
+ const int LOGON32_LOGON_INTERACTIVE = 2;
+
+ if (!LogonUser(_userName, ".", testAccountPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out _accountTokenHandle))
+ {
+ _accountTokenHandle = null;
+ throw new Exception($"Failed to get SafeAccessTokenHandle for test account {_userName}", new Win32Exception());
+ }
+
+ bool gotRef = false;
+ try
+ {
+ _accountTokenHandle.DangerousAddRef(ref gotRef);
+ IntPtr logonToken = _accountTokenHandle.DangerousGetHandle();
+ AccountName = new WindowsIdentity(logonToken).Name;
+ }
+ finally
+ {
+ if (gotRef)
+ _accountTokenHandle.DangerousRelease();
+ }
+ }
+ }
+
+ [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
+ private static extern bool LogonUser(string userName, string domain, string password, int logonType, int logonProvider, out SafeAccessTokenHandle safeAccessTokenHandle);
+
+ [DllImport("netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
+ internal static extern uint NetUserAdd([MarshalAs(UnmanagedType.LPWStr)]string servername, uint level, ref USER_INFO_1 buf, out uint parm_err);
+
+ [DllImport("netapi32.dll")]
+ internal static extern uint NetUserDel([MarshalAs(UnmanagedType.LPWStr)]string servername, [MarshalAs(UnmanagedType.LPWStr)]string username);
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
+ internal struct USER_INFO_1
+ {
+ public string usri1_name;
+ public string usri1_password;
+ public uint usri1_password_age;
+ public uint usri1_priv;
+ public string usri1_home_dir;
+ public string usri1_comment;
+ public uint usri1_flags;
+ public string usri1_script_path;
+ }
+
+ public void Dispose()
+ {
+ _accountTokenHandle?.Dispose();
+
+ uint result = NetUserDel(null, _userName);
+
+ // 2221= NERR_UserNotFound
+ if (result != 0 && result != 2221)
+ {
+ throw new Win32Exception((int)result);
+ }
+ }
+ }
+ }
+
<Compile Include="System\PlatformDetection.cs" />
<Compile Include="System\PlatformDetection.Unix.cs" />
<Compile Include="System\PlatformDetection.Windows.cs" />
+ <Compile Include="System\WindowsIdentityFixture.cs" />
<!--
Interop.Library is not designed to support runtime checks therefore we are picking the Windows
variant from the Common folder and adding the missing members manually.
allowHttp3 && allowHttp2 ? HttpVersion.Version30 :
allowHttp2 ? HttpVersion.Version20 :
HttpVersion.Version11;
- _defaultCredentialsUsedForProxy = _proxy != null && (_proxy.Credentials == CredentialCache.DefaultCredentials || _defaultProxyCredentials == CredentialCache.DefaultCredentials);
- _defaultCredentialsUsedForServer = _credentials == CredentialCache.DefaultCredentials;
}
/// <summary>Creates a copy of the settings but with some values normalized to suit the implementation.</summary>
_connectTimeout = _connectTimeout,
_credentials = _credentials,
_defaultProxyCredentials = _defaultProxyCredentials,
- _defaultCredentialsUsedForProxy = _defaultCredentialsUsedForProxy,
- _defaultCredentialsUsedForServer = _defaultCredentialsUsedForServer,
_expect100ContinueTimeout = _expect100ContinueTimeout,
_maxAutomaticRedirections = _maxAutomaticRedirections,
_maxConnectionsPerServer = _maxConnectionsPerServer,
_plaintextStreamFilter = _plaintextStreamFilter,
_initialHttp2StreamWindowSize = _initialHttp2StreamWindowSize,
_activityHeadersPropagator = _activityHeadersPropagator,
+ _defaultCredentialsUsedForProxy = _proxy != null && (_proxy.Credentials == CredentialCache.DefaultCredentials || _defaultProxyCredentials == CredentialCache.DefaultCredentials),
+ _defaultCredentialsUsedForServer = _credentials == CredentialCache.DefaultCredentials,
};
// TODO: Remove if/when QuicImplementationProvider is removed from System.Net.Quic.
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using System.Net.Security;
+using System.Net.Test.Common;
+using System.Security.Principal;
+using System.Threading.Tasks;
+
+using Xunit;
+using Xunit.Abstractions;
+
+namespace System.Net.Http.Functional.Tests
+{
+ public class ImpersonatedAuthTests: IClassFixture<WindowsIdentityFixture>
+ {
+ public static bool CanRunImpersonatedTests = PlatformDetection.IsWindows && PlatformDetection.IsNotWindowsNanoServer;
+ private readonly WindowsIdentityFixture _fixture;
+ private readonly ITestOutputHelper _output;
+
+ public ImpersonatedAuthTests(WindowsIdentityFixture windowsIdentityFixture, ITestOutputHelper output)
+ {
+ _output = output;
+ _fixture = windowsIdentityFixture;
+
+ Assert.False(_fixture.TestAccount.AccountTokenHandle.IsInvalid);
+ Assert.False(string.IsNullOrEmpty(_fixture.TestAccount.AccountName));
+ }
+
+ [OuterLoop]
+ [ConditionalTheory(nameof(CanRunImpersonatedTests))]
+ [InlineData(true)]
+ [InlineData(false)]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public async Task DefaultHandler_ImpersonificatedUser_Success(bool useNtlm)
+ {
+ await LoopbackServer.CreateClientAndServerAsync(
+ async uri =>
+ {
+ HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
+ requestMessage.Version = new Version(1, 1);
+
+ var handler = new HttpClientHandler();
+ handler.UseDefaultCredentials = true;
+
+ using (var client = new HttpClient(handler))
+ {
+ HttpResponseMessage response = await client.SendAsync(requestMessage);
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("foo", await response.Content.ReadAsStringAsync());
+
+ string initialUser = response.Headers.GetValues(NtAuthTests.UserHeaderName).First();
+
+ _output.WriteLine($"Starting test as {WindowsIdentity.GetCurrent().Name}");
+
+ // get token and run another request as different user.
+ WindowsIdentity.RunImpersonated(_fixture.TestAccount.AccountTokenHandle, () =>
+ {
+ _output.WriteLine($"Running test as {WindowsIdentity.GetCurrent().Name}");
+ Assert.Equal(_fixture.TestAccount.AccountName, WindowsIdentity.GetCurrent().Name);
+
+ requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
+ requestMessage.Version = new Version(1, 1);
+
+ HttpResponseMessage response = client.SendAsync(requestMessage).GetAwaiter().GetResult();
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ Assert.Equal("foo", response.Content.ReadAsStringAsync().GetAwaiter().GetResult());
+
+ string newUser = response.Headers.GetValues(NtAuthTests.UserHeaderName).First();
+ Assert.Equal(_fixture.TestAccount.AccountName, newUser);
+ });
+ }
+ },
+ async server =>
+ {
+ await server.AcceptConnectionAsync(async connection =>
+ {
+ Task t = useNtlm ? NtAuthTests.HandleNtlmAuthenticationRequest(connection, closeConnection: false) : NtAuthTests.HandleNegotiateAuthenticationRequest(connection, closeConnection: false);
+ await t;
+ _output.WriteLine("Finished first request");
+
+ // Second request should use new connection as it runs as different user.
+ // We keep first connection open so HttpClient may be tempted top use it.
+ await server.AcceptConnectionAsync(async connection =>
+ {
+ Task t = useNtlm ? NtAuthTests.HandleNtlmAuthenticationRequest(connection, closeConnection: false) : NtAuthTests.HandleNegotiateAuthenticationRequest(connection, closeConnection: false);
+ await t;
+ }).ConfigureAwait(false);
+ }).ConfigureAwait(false);
+ });
+
+ }
+ }
+}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using System.Net.Security;
+using System.Net.Test.Common;
+using System.Security.Principal;
+using System.Threading.Tasks;
+
+using Xunit;
+using Xunit.Abstractions;
+
+namespace System.Net.Http.Functional.Tests
+{
+ public partial class NtAuthTests : IClassFixture<NtAuthServers>
+ {
+ internal const string NtlmAuthHeader = "WWW-Authenticate: NTLM";
+ internal const string NegotiateAuthHeader = "WWW-Authenticate: Negotiate";
+ internal const string UserHeaderName = "X-User";
+
+ internal static Task HandleNtlmAuthenticationRequest(LoopbackServer.Connection connection, bool closeConnection = true)
+ {
+ return HandleAuthenticationRequest(connection, useNtlm: true, useNegotiate: false, closeConnection);
+ }
+
+ internal static Task HandleNegotiateAuthenticationRequest(LoopbackServer.Connection connection, bool closeConnection = true)
+ {
+ return HandleAuthenticationRequest(connection, useNtlm: false, useNegotiate: true, closeConnection);
+ }
+
+ internal static async Task HandleAuthenticationRequest(LoopbackServer.Connection connection, bool useNtlm, bool useNegotiate, bool closeConnection)
+ {
+ HttpRequestData request = await connection.ReadRequestDataAsync();
+ NTAuthentication authContext = null;
+ string authHeader = null;
+
+ foreach (HttpHeaderData header in request.Headers)
+ {
+ if (header.Name == "Authorization")
+ {
+ authHeader = header.Value;
+ break;
+ }
+ }
+
+ if (string.IsNullOrEmpty(authHeader))
+ {
+ // This is initial request, we reject with showing supported mechanisms.
+ authHeader = string.Empty;
+ if (useNtlm)
+ {
+ authHeader += NtlmAuthHeader + "\r\n";
+ }
+
+ if (useNegotiate)
+ {
+ authHeader += NegotiateAuthHeader + "\r\n";
+ }
+
+ await connection.SendResponseAsync(HttpStatusCode.Unauthorized, authHeader).ConfigureAwait(false);
+ connection.CompleteRequestProcessing();
+
+ // Read next requests and fall-back to loop bellow to process it.
+ request = await connection.ReadRequestDataAsync();
+ }
+
+ SecurityStatusPal statusCode;
+ do
+ {
+ foreach (HttpHeaderData header in request.Headers)
+ {
+ if (header.Name == "Authorization")
+ {
+ authHeader = header.Value;
+ break;
+ }
+ }
+
+ Assert.NotNull(authHeader);
+ var tokens = authHeader.Split(' ', 2, StringSplitOptions.TrimEntries);
+ // Should be type and base64 encoded blob
+ Assert.Equal(2, tokens.Length);
+
+ authContext ??= new NTAuthentication(isServer: true, tokens[0], CredentialCache.DefaultNetworkCredentials, null, ContextFlagsPal.Connection, null);
+
+ byte[]? outBlob = authContext.GetOutgoingBlob(Convert.FromBase64String(tokens[1]), throwOnError: false, out statusCode);
+
+ if (outBlob != null && statusCode.ErrorCode == SecurityStatusPalErrorCode.ContinueNeeded)
+ {
+ authHeader = $"WWW-Authenticate: {tokens[0]} {Convert.ToBase64String(outBlob)}\r\n";
+ await connection.SendResponseAsync(HttpStatusCode.Unauthorized, authHeader);
+ connection.CompleteRequestProcessing();
+
+ request = await connection.ReadRequestDataAsync();
+ }
+ }
+ while (statusCode.ErrorCode == SecurityStatusPalErrorCode.ContinueNeeded);
+
+ if (statusCode.ErrorCode == SecurityStatusPalErrorCode.OK)
+ {
+ // If authentication succeeded ask Windows about the identity and send it back as custom header.
+ SecurityContextTokenHandle? userContext = null;
+ using SafeDeleteContext securityContext = authContext.GetContext(out SecurityStatusPal statusCodeNew)!;
+ SSPIWrapper.QuerySecurityContextToken(GlobalSSPI.SSPIAuth, securityContext, out userContext);
+ using WindowsIdentity identity = new WindowsIdentity(userContext.DangerousGetHandle(), authContext.ProtocolName);
+
+ authHeader = $"{UserHeaderName}: {identity.Name}\r\n";
+ if (closeConnection)
+ {
+ authHeader += "Connection: close\r\n";
+ }
+
+ await connection.SendResponseAsync(HttpStatusCode.OK, authHeader, "foo");
+ userContext.Dispose();
+ }
+ else
+ {
+ await connection.SendResponseAsync(HttpStatusCode.Forbidden, "Connection: close\r\n", "boo");
+ }
+ }
+
+ [ConditionalTheory(typeof(PlatformDetection), nameof(PlatformDetection.IsWindows), nameof(PlatformDetection.IsNotWindowsNanoServer))]
+ [InlineData(true)]
+ [InlineData(false)]
+ [PlatformSpecific(TestPlatforms.Windows)]
+ public async Task DefaultHandler_DefaultCredentials_Success(bool useNtlm)
+ {
+ await LoopbackServer.CreateClientAndServerAsync(
+ async uri =>
+ {
+ HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
+ requestMessage.Version = new Version(1, 1);
+
+ var handler = new HttpClientHandler() { UseDefaultCredentials = true };
+ using (var client = new HttpClient(handler))
+ {
+ HttpResponseMessage response = await client.SendAsync(requestMessage);
+ Assert.Equal(HttpStatusCode.OK, response.StatusCode);
+ _output.WriteLine($"Authenticated as {response.Headers.GetValues(NtAuthTests.UserHeaderName).First()}");
+ Assert.Equal("foo", await response.Content.ReadAsStringAsync());
+ }
+ },
+ async server =>
+ {
+ await server.AcceptConnectionAsync(async connection =>
+ {
+ Task t = useNtlm ? HandleNtlmAuthenticationRequest(connection) : HandleNegotiateAuthenticationRequest(connection);
+ await t;
+ }).ConfigureAwait(false);
+ });
+ }
+ }
+}
}
}
- public class NtAuthTests : IClassFixture<NtAuthServers>
+ public partial class NtAuthTests : IClassFixture<NtAuthServers>
{
private readonly NtAuthServers _servers;
private readonly ITestOutputHelper _output;
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<StringResourcesPath>../../src/Resources/Strings.resx</StringResourcesPath>
<DefineConstants Condition="'$(TargetsWindows)'=='true'">$(DefineConstants);TargetsWindows</DefineConstants>
<ItemGroup>
<Compile Include="$(CommonPath)Interop\Unix\Interop.Libraries.cs" Condition="'$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true'"
Link="Common\Interop\Unix\Interop.Libraries.cs" />
- <Compile Include="$(CommonPath)System\Net\Http\aspnetcore\NetEventSource.Common.cs"
- Link="Common\System\Net\Http\aspnetcore\NetEventSource.Common.cs" />
+ <Compile Include="$(CommonPath)System\Net\Logging\NetEventSource.Common.cs"
+ Link="Common\System\Net\Logging\NetEventSource.Common.cs" />
<Compile Include="$(CommonPath)System\Threading\Tasks\TaskToApm.cs"
Link="System\System\Threading\Tasks\TaskToApm.cs" />
<Compile Include="$(CommonPath)Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.IsNtlmInstalled.cs" Condition="'$(TargetsUnix)' == 'true' or '$(TargetsBrowser)' == 'true'"
<ItemGroup Condition=" '$(TargetsWindows)' == 'true'">
<Compile Include="$(CommonPath)Interop\Windows\Interop.Libraries.cs"
Link="Common\Interop\Windows\Interop.Libraries.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.UNICODE_STRING.cs"
+ Link="Common\Interop\Windows\Interop.UNICODE_STRING.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Interop.BOOL.cs"
+ Link="Common\Interop\Windows\Interop.BOOL.cs" />
+ <Compile Include="$(CommonPath)System\Net\DebugSafeHandle.cs"
+ Link="Common\System\Net\DebugSafeHandle.cs" />
+ <Compile Include="$(CommonPath)System\Net\InternalException.cs"
+ Link="Common\System\Net\InternalException.cs" />
+ <Compile Include="$(CommonPath)System\Net\ExceptionCheck.cs"
+ Link="Common\System\Net\ExceptionCheck.cs" />
+ <!-- Add NTAuthentication -->
+ <Compile Include="$(CommonPath)System\Net\DebugCriticalHandleMinusOneIsInvalid.cs"
+ Link="Common\System\Net\DebugCriticalHandleMinusOneIsInvalid.cs" />
+ <Compile Include="$(CommonPath)System\Net\DebugCriticalHandleZeroOrMinusOneIsInvalid.cs"
+ Link="Common\System\Net\DebugCriticalHandleZeroOrMinusOneIsInvalid.cs" />
+ <Compile Include="$(CommonPath)System\Collections\Generic\BidirectionalDictionary.cs"
+ Link="Common\System\Collections\Generic\BidirectionalDictionary.cs" />
+ <Compile Include="$(CommonPath)System\NotImplemented.cs"
+ Link="Common\System\NotImplemented.cs" />
+ <Compile Include="$(CommonPath)System\Net\ContextFlagsPal.cs"
+ Link="Common\System\Net\ContextFlagsPal.cs" />
+ <Compile Include="$(CommonPath)System\Net\NegotiationInfoClass.cs"
+ Link="Common\System\Net\NegotiationInfoClass.cs" />
+ <Compile Include="$(CommonPath)System\Net\NTAuthentication.Common.cs"
+ Link="Common\System\Net\NTAuthentication.Common.cs" />
+ <Compile Include="$(CommonPath)System\Net\SecurityStatusPal.cs"
+ Link="Common\System\Net\SecurityStatusPal.cs" />
+ <Compile Include="$(CommonPath)System\Net\Security\SecurityBuffer.Windows.cs"
+ Link="Common\System\Net\Security\SecurityBuffer.Windows.cs" />
+ <Compile Include="$(CommonPath)System\Net\Security\SecurityBufferType.Windows.cs"
+ Link="Common\System\Net\Security\SecurityBufferType.Windows.cs" />
+ <Compile Include="$(CommonPath)System\Net\Security\SafeCredentialReference.cs"
+ Link="Common\System\Net\Security\SafeCredentialReference.cs" />
+ <Compile Include="$(CommonPath)System\Net\Security\SSPIHandleCache.cs"
+ Link="Common\System\Net\Security\SSPIHandleCache.cs" />
+ <Compile Include="$(CommonPath)System\Net\Security\NetEventSource.Security.cs"
+ Link="Common\System\Net\Security\NetEventSource.Security.cs" />
+ <!-- Windows specific NTAuthentication -->
+ <Compile Include="$(CommonPath)System\Net\Security\SecurityContextTokenHandle.cs"
+ Link="Common\System\Net\Security\SecurityContextTokenHandle.cs" />
+ <Compile Include="$(CommonPath)System\Net\SecurityStatusAdapterPal.Windows.cs"
+ Link="Common\System\Net\SecurityStatusAdapterPal.Windows.cs" />
+ <Compile Include="$(CommonPath)System\Net\ContextFlagsAdapterPal.Windows.cs"
+ Link="Common\System\Net\ContextFlagsAdapterPal.Windows.cs" />
+ <Compile Include="$(CommonPath)System\Net\Security\NegotiateStreamPal.Windows.cs"
+ Link="Common\System\Net\Security\NegotiateStreamPal.Windows.cs" />
+ <Compile Include="$(CommonPath)System\Net\Security\NetEventSource.Security.Windows.cs"
+ Link="Common\System\Net\Security\NetEventSource.Security.Windows.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CERT_CONTEXT.cs"
+ Link="Common\Interop\Windows\Crypt32\Interop.CERT_CONTEXT.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CertFreeCertificateContext.cs"
+ Link="Common\Interop\Windows\Crypt32\Interop.Interop.CertFreeCertificateContext.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CERT_INFO.cs"
+ Link="Common\Interop\Windows\Crypt32\Interop.CERT_INFO.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CERT_PUBLIC_KEY_INFO.cs"
+ Link="Common\Interop\Windows\Crypt32\Interop.CERT_PUBLIC_KEY_INFO.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CRYPT_ALGORITHM_IDENTIFIER.cs"
+ Link="Common\Interop\Windows\Crypt32\Interop.Interop.CRYPT_ALGORITHM_IDENTIFIER.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.CRYPT_BIT_BLOB.cs"
+ Link="Common\Interop\Windows\Crypt32\Interop.Interop.CRYPT_BIT_BLOB.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.DATA_BLOB.cs"
+ Link="Common\Interop\Windows\Crypt32\Interop.DATA_BLOB.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Crypt32\Interop.MsgEncodingType.cs"
+ Link="Common\Interop\Windows\Crypt32\Interop.Interop.MsgEncodingType.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SecPkgContext_Bindings.cs"
+ Link="Common\Interop\Windows\SspiCli\SecPkgContext_Bindings.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SChannel\Interop.SECURITY_STATUS.cs"
+ Link="Common\Interop\Windows\SChannel\Interop.SECURITY_STATUS.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\Kernel32\Interop.CloseHandle.cs"
+ Link="Common\Interop\Windows\Kernel32\Interop.CloseHandle.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SecPkgContext_StreamSizes.cs"
+ Link="Common\Interop\Windows\SspiCli\SecPkgContext_StreamSizes.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SecPkgContext_NegotiationInfoW.cs"
+ Link="Common\Interop\Windows\SspiCli\SecPkgContext_NegotiationInfoW.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\NegotiationInfoClass.cs"
+ Link="Common\Interop\Windows\SspiCli\NegotiationInfoClass.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SChannel\SecPkgContext_ConnectionInfo.cs"
+ Link="Common\Interop\Windows\SChannel\SecPkgContext_ConnectionInfo.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SChannel\SecPkgContext_CipherInfo.cs"
+ Link="Common\Interop\Windows\SChannel\SecPkgContext_CipherInfo.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SSPISecureChannelType.cs"
+ Link="Common\Interop\Windows\SspiCli\SSPISecureChannelType.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\ISSPIInterface.cs"
+ Link="Common\Interop\Windows\SspiCli\ISSPIInterface.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SSPIAuthType.cs"
+ Link="Common\Interop\Windows\SspiCli\SSPIAuthType.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SecurityPackageInfoClass.cs"
+ Link="Common\Interop\Windows\SspiCli\SecurityPackageInfoClass.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SecurityPackageInfo.cs"
+ Link="Common\Interop\Windows\SspiCli\SecurityPackageInfo.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SecPkgContext_Sizes.cs"
+ Link="Common\Interop\Windows\SspiCli\SecPkgContext_Sizes.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SafeDeleteContext.cs"
+ Link="Common\Interop\Windows\SspiCli\SafeDeleteContext.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\GlobalSSPI.cs"
+ Link="Common\Interop\Windows\SspiCli\GlobalSSPI.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\Interop.SSPI.cs"
+ Link="Common\Interop\Windows\SspiCli\Interop.SSPI.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SecuritySafeHandles.cs"
+ Link="Common\Interop\Windows\SspiCli\SecuritySafeHandles.cs" />
+ <Compile Include="$(CommonPath)Interop\Windows\SspiCli\SSPIWrapper.cs"
+ Link="Common\Interop\Windows\SspiCli\SSPIWrapper.cs" />
+ <Compile Include="NtAuthTests.Windows.cs" />
+ <Compile Include="ImpersonatedAuthTests.cs" />
</ItemGroup>
<!-- Linux specific files -->
<ItemGroup Condition="'$(TargetsLinux)' == 'true' or '$(TargetsBrowser)' == 'true'">
{
WindowsIdentity currentWindowsIdentity = WindowsIdentity.GetCurrent();
+ // make sure the assembly is loaded.
+ _ = Dns.GetHostAddresses("");
+
await WindowsIdentity.RunImpersonatedAsync(_fixture.TestAccount.AccountTokenHandle, async () =>
{
Assert.Equal(_fixture.TestAccount.AccountName, WindowsIdentity.GetCurrent().Name);
});
}
}
-
-public class WindowsIdentityFixture : IDisposable
-{
- public WindowsTestAccount TestAccount { get; private set; }
-
- public WindowsIdentityFixture()
- {
- TestAccount = new WindowsTestAccount("CorFxTstWiIde01kiu");
- }
-
- public void Dispose()
- {
- TestAccount.Dispose();
- }
-}
-
-public sealed class WindowsTestAccount : IDisposable
-{
- private readonly string _userName;
- private SafeAccessTokenHandle _accountTokenHandle;
- public SafeAccessTokenHandle AccountTokenHandle => _accountTokenHandle;
- public string AccountName { get; private set; }
-
- public WindowsTestAccount(string userName)
- {
- _userName = userName;
- CreateUser();
- }
-
- private void CreateUser()
- {
- string testAccountPassword;
- using (RandomNumberGenerator rng = RandomNumberGenerator.Create())
- {
- byte[] randomBytes = new byte[33];
- rng.GetBytes(randomBytes);
-
- // Add special chars to ensure it satisfies password requirements.
- testAccountPassword = Convert.ToBase64String(randomBytes) + "_-As@!%*(1)4#2";
-
- USER_INFO_1 userInfo = new USER_INFO_1
- {
- usri1_name = _userName,
- usri1_password = testAccountPassword,
- usri1_priv = 1
- };
-
- // Create user and remove/create if already exists
- uint result = NetUserAdd(null, 1, ref userInfo, out uint param_err);
-
- // error codes https://docs.microsoft.com/en-us/windows/desktop/netmgmt/network-management-error-codes
- // 0 == NERR_Success
- if (result == 2224) // NERR_UserExists
- {
- result = NetUserDel(null, userInfo.usri1_name);
- if (result != 0)
- {
- throw new Win32Exception((int)result);
- }
- result = NetUserAdd(null, 1, ref userInfo, out param_err);
- if (result != 0)
- {
- throw new Win32Exception((int)result);
- }
- }
-
- const int LOGON32_PROVIDER_DEFAULT = 0;
- const int LOGON32_LOGON_INTERACTIVE = 2;
-
- if (!LogonUser(_userName, ".", testAccountPassword, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, out _accountTokenHandle))
- {
- _accountTokenHandle = null;
- throw new Exception($"Failed to get SafeAccessTokenHandle for test account {_userName}", new Win32Exception());
- }
-
- bool gotRef = false;
- try
- {
- _accountTokenHandle.DangerousAddRef(ref gotRef);
- IntPtr logonToken = _accountTokenHandle.DangerousGetHandle();
- AccountName = new WindowsIdentity(logonToken).Name;
- }
- finally
- {
- if (gotRef)
- _accountTokenHandle.DangerousRelease();
- }
- }
- }
-
- [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
- private static extern bool LogonUser(string userName, string domain, string password, int logonType, int logonProvider, out SafeAccessTokenHandle safeAccessTokenHandle);
-
- [DllImport("netapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
- internal static extern uint NetUserAdd([MarshalAs(UnmanagedType.LPWStr)]string servername, uint level, ref USER_INFO_1 buf, out uint parm_err);
-
- [DllImport("netapi32.dll")]
- internal static extern uint NetUserDel([MarshalAs(UnmanagedType.LPWStr)]string servername, [MarshalAs(UnmanagedType.LPWStr)]string username);
-
- [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
- internal struct USER_INFO_1
- {
- public string usri1_name;
- public string usri1_password;
- public uint usri1_password_age;
- public uint usri1_priv;
- public string usri1_home_dir;
- public string usri1_comment;
- public uint usri1_flags;
- public string usri1_script_path;
- }
-
- public void Dispose()
- {
- _accountTokenHandle?.Dispose();
-
- uint result = NetUserDel(null, _userName);
-
- // 2221= NERR_UserNotFound
- if (result != 0 && result != 2221)
- {
- throw new Win32Exception((int)result);
- }
- }
-}
-