From 942537e64fc52c032214de3103c4fc241565e236 Mon Sep 17 00:00:00 2001 From: Chris R Date: Fri, 5 Apr 2019 14:32:53 -0700 Subject: [PATCH 01/17] Minimal test case --- .../Net/Security/NegotiateStreamPal.Unix.cs | 5 - .../NegotiateStreamKerberosTest.cs | 114 ++++++++++++++++-- 2 files changed, 107 insertions(+), 12 deletions(-) diff --git a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 7ba998e3b065..a6a0ce37df55 100644 --- a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -314,11 +314,6 @@ internal static SafeFreeCredentials AcquireDefaultCredential(string package, boo internal static SafeFreeCredentials AcquireCredentialsHandle(string package, bool isServer, NetworkCredential credential) { - if (isServer) - { - throw new PlatformNotSupportedException(SR.net_nego_server_not_supported); - } - bool isEmptyCredential = string.IsNullOrWhiteSpace(credential.UserName) || string.IsNullOrWhiteSpace(credential.Password); bool ntlmOnly = string.Equals(package, NegotiationInfoClass.NTLM, StringComparison.OrdinalIgnoreCase); diff --git a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs index be31a9948b9c..2c695b8d92c8 100644 --- a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs @@ -27,6 +27,7 @@ public static IEnumerable GoodCredentialsData get { yield return new object[] { CredentialCache.DefaultNetworkCredentials }; + /* yield return new object[] { new NetworkCredential( Configuration.Security.ActiveDirectoryUserName, Configuration.Security.ActiveDirectoryUserPassword, @@ -59,6 +60,7 @@ public static IEnumerable GoodCredentialsData Configuration.Security.ActiveDirectoryUserName, (SecureString)null, null) }; + */ } } @@ -90,24 +92,24 @@ public static IEnumerable BadCredentialsData } [OuterLoop] - [ConditionalTheory(nameof(IsServerAndDomainAvailable))] + // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] [MemberData(nameof(GoodCredentialsData))] - public async Task NegotiateStream_AuthenticationRemote_Success(object credentialObject) + public async Task NegotiateStream_ClientAuthenticationRemote_Success(object credentialObject) { var credential = (NetworkCredential)credentialObject; - await VerifyAuthentication(credential); + await VerifyClientAuthentication(credential); } [OuterLoop] - [ConditionalTheory(nameof(IsServerAndDomainAvailable))] + // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] [MemberData(nameof(BadCredentialsData))] - public async Task NegotiateStream_AuthenticationRemote_Fails(object credentialObject) + public async Task NegotiateStream_ClientAuthenticationRemote_Fails(object credentialObject) { var credential = (NetworkCredential)credentialObject; - await Assert.ThrowsAsync(() => VerifyAuthentication(credential)); + await Assert.ThrowsAsync(() => VerifyClientAuthentication(credential)); } - private async Task VerifyAuthentication(NetworkCredential credential) + private async Task VerifyClientAuthentication(NetworkCredential credential) { string serverName = Configuration.Security.NegotiateServer.Host; int port = Configuration.Security.NegotiateServer.Port; @@ -157,6 +159,104 @@ await auth.AuthenticateAsClientAsync( } } + [OuterLoop] + [Theory] + // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] + [MemberData(nameof(GoodCredentialsData))] + public async Task NegotiateStream_ServerAuthenticationRemote_Success(object credentialObject) + { + var credential = (NetworkCredential)credentialObject; + await VerifyLocalServerAuthentication(credential); + } + + [OuterLoop] + // [Theory] + // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] + [MemberData(nameof(BadCredentialsData))] + public async Task NegotiateStream_ServerAuthenticationRemote_Fails(object credentialObject) + { + var credential = (NetworkCredential)credentialObject; + await Assert.ThrowsAsync(() => VerifyLocalServerAuthentication(credential)); + } + + private async Task VerifyLocalServerAuthentication(NetworkCredential credential) + { + string serverName = "CHRROSS-UDESK.CRKERBEROS.COM";// Configuration.Security.NegotiateServer.Host; + int port = 54321;// Configuration.Security.NegotiateServer.Port; + string serverSPN = "host/" + serverName; + + string expectedAuthenticationType = "Kerberos"; + bool mutualAuthenitcated = true; +/* + bool isLocalhost = false; // await IsLocalHost(serverName); + if (credential == CredentialCache.DefaultNetworkCredentials && isLocalhost) + { + expectedAuthenticationType = "NTLM"; + } + else if (credential != CredentialCache.DefaultNetworkCredentials && + (string.IsNullOrEmpty(credential.UserName) || string.IsNullOrEmpty(credential.Password))) + { + // Anonymous authentication. + expectedAuthenticationType = "NTLM"; + mutualAuthenitcated = false; + } +*/ + using (var client = new TcpClient()) + { + var server = new TcpListener(IPAddress.Loopback, port); + server.Start(); + try + { + var acceptTask = server.AcceptTcpClientAsync(); + await client.ConnectAsync(IPAddress.Loopback, port); + + var serverConnection = await acceptTask; + var serverStream = serverConnection.GetStream(); + using (var serverAuth = new NegotiateStream(serverStream, leaveInnerStreamOpen: false)) + { + var clientStream = client.GetStream(); + using (var clientAuth = new NegotiateStream(clientStream, leaveInnerStreamOpen:false)) + { + var serverAuthTask = serverAuth.AuthenticateAsServerAsync(); + var clientAuthTask = clientAuth.AuthenticateAsClientAsync( + credential, + serverSPN, + ProtectionLevel.EncryptAndSign, + System.Security.Principal.TokenImpersonationLevel.Identification); + + await serverAuthTask; + + Assert.Equal(expectedAuthenticationType, serverAuth.RemoteIdentity.AuthenticationType); + Assert.Equal(serverSPN, serverAuth.RemoteIdentity.Name); + + Assert.Equal(true, serverAuth.IsAuthenticated); + Assert.Equal(true, serverAuth.IsEncrypted); + Assert.Equal(mutualAuthenitcated, serverAuth.IsMutuallyAuthenticated); + Assert.Equal(true, serverAuth.IsSigned); + + await clientAuthTask; + + Assert.Equal(expectedAuthenticationType, clientAuth.RemoteIdentity.AuthenticationType); + Assert.Equal(serverSPN, clientAuth.RemoteIdentity.Name); + + Assert.Equal(true, clientAuth.IsAuthenticated); + Assert.Equal(true, clientAuth.IsEncrypted); + Assert.Equal(mutualAuthenitcated, clientAuth.IsMutuallyAuthenticated); + Assert.Equal(true, clientAuth.IsSigned); + + // Send a message to the server. Encode the test data into a byte array. + byte[] message = Encoding.UTF8.GetBytes("Hello from the client."); + await clientAuth.WriteAsync(message, 0, message.Length); + } + } + } + finally + { + server.Stop(); + } + } + } + private async static Task IsLocalHost(string hostname) { IPAddress[] hostAddresses = await Dns.GetHostAddressesAsync(hostname); From 055e309b39d8d4b36d8f76cb19c217504ca80145 Mon Sep 17 00:00:00 2001 From: Chris R Date: Fri, 5 Apr 2019 16:25:11 -0700 Subject: [PATCH 02/17] AcceptSecurityContext --- .../Net/Security/NegotiateStreamPal.Unix.cs | 108 +++++++++++++++++- .../Security/Unix/SafeDeleteNegoContext.cs | 7 +- 2 files changed, 113 insertions(+), 2 deletions(-) diff --git a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index a6a0ce37df55..15a0d4fc0fea 100644 --- a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -165,6 +165,45 @@ private static bool GssInitSecurityContext( return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE; } + private static bool GssAcceptSecurityContext( + ref SafeGssContextHandle context, + byte[] buffer, + out byte[] outputBuffer) + { + // do we need to initalize this? + if (context == null) + { + context = new SafeGssContextHandle(); + } + + Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer); + Interop.NetSecurityNative.Status status; + + try + { + Interop.NetSecurityNative.Status minorStatus; + status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus, + ref context, + buffer, + buffer?.Length ?? 0, + ref token); + + if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) && + (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED)) + { + throw new Interop.NetSecurityNative.GssApiException(status, minorStatus); + } + + outputBuffer = token.ToByteArray(); + } + finally + { + token.Dispose(); + } + + return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE; + } + private static SecurityStatusPal EstablishSecurityContext( SafeFreeNegoCredentials credential, ref SafeDeleteContext context, @@ -293,7 +332,74 @@ internal static SecurityStatusPal AcceptSecurityContext( ref byte[] resultBlob, ref ContextFlagsPal contextFlags) { - throw new PlatformNotSupportedException(SR.net_nego_server_not_supported); + // bool isNtlmOnly = credential.IsNtlmOnly; + + if (securityContext == null) + { + /* + if (NetEventSource.IsEnabled) + { + string protocol = isNtlmOnly ? "NTLM" : "SPNEGO"; + NetEventSource.Info(null, $"requested protocol = {protocol}, target = {targetName}"); + } + */ + securityContext = new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle); + } + + if (credentialsHandle == null) + { + throw new ArgumentNullException(nameof(credentialsHandle)); + } + if (securityContext == null) + { + throw new ArgumentNullException(nameof(securityContext)); + } + if (incomingBlob == null) + { + throw new ArgumentNullException(nameof(incomingBlob)); + } + + SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext; + try + { + SafeGssContextHandle contextHandle = negoContext.GssContext; + bool done = GssAcceptSecurityContext( + ref contextHandle, + incomingBlob, + out resultBlob); +/* + if (done) + { + if (NetEventSource.IsEnabled) + { + string protocol = isNtlmOnly ? "NTLM" : isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos"; + NetEventSource.Info(null, $"actual protocol = {protocol}"); + } + + // Populate protocol used for authentication + negoContext.SetAuthenticationPackage(isNtlmUsed); + } +*/ + Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi"); + Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext); + + // Save the inner context handle for further calls to NetSecurity + Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext); + if (null == negoContext.GssContext) + { + negoContext.SetGssContext(contextHandle); + } + + SecurityStatusPalErrorCode errorCode = done ? + (negoContext.IsNtlmUsed && resultBlob.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) : + SecurityStatusPalErrorCode.ContinueNeeded; + return new SecurityStatusPal(errorCode); + } + catch (Exception ex) + { + if (NetEventSource.IsEnabled) NetEventSource.Error(null, ex); + return new SecurityStatusPal(SecurityStatusPalErrorCode.InternalError, ex); + } } internal static Win32Exception CreateExceptionFromError(SecurityStatusPal statusCode) diff --git a/src/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs b/src/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs index 58cc7d68bb83..935ca3786413 100644 --- a/src/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs +++ b/src/Common/src/System/Net/Security/Unix/SafeDeleteNegoContext.cs @@ -32,10 +32,15 @@ public SafeGssContextHandle GssContext get { return _context; } } - public SafeDeleteNegoContext(SafeFreeNegoCredentials credential, string targetName) + public SafeDeleteNegoContext(SafeFreeNegoCredentials credential) : base(credential) { Debug.Assert((null != credential), "Null credential in SafeDeleteNegoContext"); + } + + public SafeDeleteNegoContext(SafeFreeNegoCredentials credential, string targetName) + : this(credential) + { try { // Convert any "SERVICE/HOST" style of targetName to use "SERVICE@HOST" style. From 87031a1a7d9fbba62804cd2f0c302714f1a3415e Mon Sep 17 00:00:00 2001 From: Chris R Date: Mon, 8 Apr 2019 16:13:26 -0700 Subject: [PATCH 03/17] Finish handshake --- .../src/System/Net/NTAuthentication.Common.cs | 3 ++- .../FunctionalTests/NegotiateStreamKerberosTest.cs | 13 ++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/Common/src/System/Net/NTAuthentication.Common.cs b/src/Common/src/System/Net/NTAuthentication.Common.cs index 64eb87d98ba2..20a4f40da931 100644 --- a/src/Common/src/System/Net/NTAuthentication.Common.cs +++ b/src/Common/src/System/Net/NTAuthentication.Common.cs @@ -292,7 +292,8 @@ internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out Secu } // The return value will tell us correctly if the handshake is over or not - if (statusCode.ErrorCode == SecurityStatusPalErrorCode.OK) + if (statusCode.ErrorCode == SecurityStatusPalErrorCode.OK + || statusCode.ErrorCode == SecurityStatusPalErrorCode.CompleteNeeded) // Server only { // Success. _isCompleted = true; diff --git a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs index 2c695b8d92c8..3576cd2efa68 100644 --- a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs @@ -181,9 +181,9 @@ public async Task NegotiateStream_ServerAuthenticationRemote_Fails(object creden private async Task VerifyLocalServerAuthentication(NetworkCredential credential) { - string serverName = "CHRROSS-UDESK.CRKERBEROS.COM";// Configuration.Security.NegotiateServer.Host; + string serverName = "chrross-udesk.crkerberos.com";// Configuration.Security.NegotiateServer.Host; int port = 54321;// Configuration.Security.NegotiateServer.Port; - string serverSPN = "host/" + serverName; + string serverSPN = "HTTP/" + serverName; string expectedAuthenticationType = "Kerberos"; bool mutualAuthenitcated = true; @@ -212,6 +212,13 @@ private async Task VerifyLocalServerAuthentication(NetworkCredential credential) var serverConnection = await acceptTask; var serverStream = serverConnection.GetStream(); + + var loop = true; + while (loop) + { + await Task.Delay(1000); + } + using (var serverAuth = new NegotiateStream(serverStream, leaveInnerStreamOpen: false)) { var clientStream = client.GetStream(); @@ -221,7 +228,7 @@ private async Task VerifyLocalServerAuthentication(NetworkCredential credential) var clientAuthTask = clientAuth.AuthenticateAsClientAsync( credential, serverSPN, - ProtectionLevel.EncryptAndSign, + ProtectionLevel.None, System.Security.Principal.TokenImpersonationLevel.Identification); await serverAuthTask; From 2dc068d7abe1f9672a3f9b3797405a0e527b7f82 Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 10 Apr 2019 15:59:04 -0700 Subject: [PATCH 04/17] MVP --- .../Net/Security/NegotiateStreamPal.Unix.cs | 2 +- .../NegotiateStreamKerberosTest.cs | 95 +++++++++++++++++-- 2 files changed, 89 insertions(+), 8 deletions(-) diff --git a/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 1232bfc0571b..917bd3815328 100644 --- a/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -24,7 +24,7 @@ internal static partial class NegotiateStreamPal { internal static IIdentity GetIdentity(NTAuthentication context) { - Debug.Assert(!context.IsServer, "GetIdentity: Server is not supported"); + if (context.IsServer) throw new PlatformNotSupportedException("GetIdentity: Server is not supported"); string name = context.Spn; string protocol = context.ProtocolName; diff --git a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs index 3576cd2efa68..6a18184f0a78 100644 --- a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs @@ -8,6 +8,8 @@ using System.Net.Test.Common; using System.Security; using System.Security.Authentication; +using System.Security.Authentication.ExtendedProtection; +using System.Security.Principal; using System.Text; using System.Threading.Tasks; @@ -160,10 +162,10 @@ await auth.AuthenticateAsClientAsync( } [OuterLoop] - [Theory] + // [Theory] // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] [MemberData(nameof(GoodCredentialsData))] - public async Task NegotiateStream_ServerAuthenticationRemote_Success(object credentialObject) + public async Task NegotiateStream_LocalServerAuthentication_Success(object credentialObject) { var credential = (NetworkCredential)credentialObject; await VerifyLocalServerAuthentication(credential); @@ -173,7 +175,7 @@ public async Task NegotiateStream_ServerAuthenticationRemote_Success(object cred // [Theory] // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] [MemberData(nameof(BadCredentialsData))] - public async Task NegotiateStream_ServerAuthenticationRemote_Fails(object credentialObject) + public async Task NegotiateStream_LocalServerAuthentication_Fails(object credentialObject) { var credential = (NetworkCredential)credentialObject; await Assert.ThrowsAsync(() => VerifyLocalServerAuthentication(credential)); @@ -203,22 +205,22 @@ private async Task VerifyLocalServerAuthentication(NetworkCredential credential) */ using (var client = new TcpClient()) { - var server = new TcpListener(IPAddress.Loopback, port); + var server = new TcpListener(IPAddress.Parse("10.121.100.113"), port); server.Start(); try { var acceptTask = server.AcceptTcpClientAsync(); - await client.ConnectAsync(IPAddress.Loopback, port); + await client.ConnectAsync(IPAddress.Parse("10.121.100.113"), port); var serverConnection = await acceptTask; var serverStream = serverConnection.GetStream(); - +/* var loop = true; while (loop) { await Task.Delay(1000); } - +*/ using (var serverAuth = new NegotiateStream(serverStream, leaveInnerStreamOpen: false)) { var clientStream = client.GetStream(); @@ -264,6 +266,85 @@ private async Task VerifyLocalServerAuthentication(NetworkCredential credential) } } + [OuterLoop] + [Theory] + // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] + [MemberData(nameof(GoodCredentialsData))] + public async Task NegotiateStream_RemoteServerAuthentication_Success(object credentialObject) + { + var credential = (NetworkCredential)credentialObject; + await VerifyRemoteServerAuthentication(credential); + } + + [OuterLoop] + // [Theory] + // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] + [MemberData(nameof(BadCredentialsData))] + public async Task NegotiateStream_RemoteServerAuthentication_Fails(object credentialObject) + { + var credential = (NetworkCredential)credentialObject; + await Assert.ThrowsAsync(() => VerifyRemoteServerAuthentication(credential)); + } + + private async Task VerifyRemoteServerAuthentication(NetworkCredential credential) + { + string serverName = "chrross-udesk.crkerberos.com";// Configuration.Security.NegotiateServer.Host; + int port = 54321;// Configuration.Security.NegotiateServer.Port; + string serverSPN = "HTTP/" + serverName; + + string expectedAuthenticationType = "Kerberos"; + bool mutualAuthenitcated = true; +/* + if (credential != CredentialCache.DefaultNetworkCredentials && + (string.IsNullOrEmpty(credential.UserName) || string.IsNullOrEmpty(credential.Password))) + { + // Anonymous authentication. + expectedAuthenticationType = "NTLM"; + mutualAuthenitcated = false; + } +*/ + var server = new TcpListener(IPAddress.Parse("10.121.100.113"), port); + server.Start(); + try + { + var acceptTask = server.AcceptTcpClientAsync(); + // TODO: Signal client to connect + + var serverConnection = await acceptTask; + var serverStream = serverConnection.GetStream(); +/* + var loop = true; + while (loop) + { + await Task.Delay(1000); + } +*/ + using (var serverAuth = new NegotiateStream(serverStream, leaveInnerStreamOpen: false)) + { + serverAuth.AuthenticateAsServer( + CredentialCache.DefaultNetworkCredentials, + ProtectionLevel.None, + TokenImpersonationLevel.Identification); + + Assert.Equal(expectedAuthenticationType, serverAuth.RemoteIdentity.AuthenticationType); + Assert.Equal(serverSPN, serverAuth.RemoteIdentity.Name); + + Assert.True(serverAuth.IsAuthenticated, "IsAuthenticated"); + Assert.True(serverAuth.IsEncrypted, "IsEncrypted"); + Assert.True(serverAuth.IsSigned, "IsSigned"); + Assert.Equal(mutualAuthenitcated, serverAuth.IsMutuallyAuthenticated); + + // Send a message to the server. Encode the test data into a byte array. + byte[] message = Encoding.UTF8.GetBytes("Hello from the server."); + await serverAuth.WriteAsync(message, 0, message.Length); + } + } + finally + { + server.Stop(); + } + } + private async static Task IsLocalHost(string hostname) { IPAddress[] hostAddresses = await Dns.GetHostAddressesAsync(hostname); From 46e60d1da78b441ffd828b9e37249081f3f74dcd Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 11 Apr 2019 09:27:00 -0700 Subject: [PATCH 05/17] Get flags --- .../Interop.NetSecurityNative.cs | 3 ++- .../Net/Security/NegotiateStreamPal.Unix.cs | 17 +++++++++++------ .../System.Net.Security.Native/pal_gssapi.c | 5 +++-- .../System.Net.Security.Native/pal_gssapi.h | 3 ++- .../NegotiateStreamKerberosTest.cs | 8 ++++---- 5 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs index 0079762a8907..b8f4e37c74e2 100644 --- a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs +++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs @@ -90,7 +90,8 @@ internal static extern Status AcceptSecContext( ref SafeGssContextHandle acceptContextHandle, byte[] inputBytes, int inputLength, - ref GssBuffer token); + ref GssBuffer token, + out uint retFlags); [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_DeleteSecContext")] internal static extern Status DeleteSecContext( diff --git a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 15a0d4fc0fea..7e4bfe706fd6 100644 --- a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -168,7 +168,8 @@ private static bool GssInitSecurityContext( private static bool GssAcceptSecurityContext( ref SafeGssContextHandle context, byte[] buffer, - out byte[] outputBuffer) + out byte[] outputBuffer, + out uint outFlags) { // do we need to initalize this? if (context == null) @@ -183,10 +184,11 @@ private static bool GssAcceptSecurityContext( { Interop.NetSecurityNative.Status minorStatus; status = Interop.NetSecurityNative.AcceptSecContext(out minorStatus, - ref context, - buffer, - buffer?.Length ?? 0, - ref token); + ref context, + buffer, + buffer?.Length ?? 0, + ref token, + out outFlags); if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) && (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED)) @@ -366,7 +368,8 @@ internal static SecurityStatusPal AcceptSecurityContext( bool done = GssAcceptSecurityContext( ref contextHandle, incomingBlob, - out resultBlob); + out resultBlob, + out uint outputFlags); /* if (done) { @@ -390,6 +393,8 @@ internal static SecurityStatusPal AcceptSecurityContext( negoContext.SetGssContext(contextHandle); } + contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop( + (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true); SecurityStatusPalErrorCode errorCode = done ? (negoContext.IsNtlmUsed && resultBlob.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) : SecurityStatusPalErrorCode.ContinueNeeded; diff --git a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c index 8c614d3fab71..9ad90d84a238 100644 --- a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c +++ b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c @@ -247,7 +247,8 @@ uint32_t NetSecurityNative_AcceptSecContext(uint32_t* minorStatus, GssCtxId** contextHandle, uint8_t* inputBytes, uint32_t inputLength, - PAL_GssBuffer* outBuffer) + PAL_GssBuffer* outBuffer, + uint32_t* retFlags) { assert(minorStatus != NULL); assert(contextHandle != NULL); @@ -266,7 +267,7 @@ uint32_t NetSecurityNative_AcceptSecContext(uint32_t* minorStatus, NULL, NULL, &gssBuffer, - 0, + retFlags, NULL, NULL); diff --git a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h index 7ebbf3dc8956..8e3720669c7d 100644 --- a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h +++ b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h @@ -124,7 +124,8 @@ DLLEXPORT uint32_t NetSecurityNative_AcceptSecContext(uint32_t* minorStatus, GssCtxId** contextHandle, uint8_t* inputBytes, uint32_t inputLength, - PAL_GssBuffer* outBuffer); + PAL_GssBuffer* outBuffer, + uint32_t* retFlags); /* diff --git a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs index 6a18184f0a78..65ab540e1d7f 100644 --- a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs @@ -323,17 +323,17 @@ private async Task VerifyRemoteServerAuthentication(NetworkCredential credential { serverAuth.AuthenticateAsServer( CredentialCache.DefaultNetworkCredentials, - ProtectionLevel.None, + ProtectionLevel.EncryptAndSign, TokenImpersonationLevel.Identification); - Assert.Equal(expectedAuthenticationType, serverAuth.RemoteIdentity.AuthenticationType); - Assert.Equal(serverSPN, serverAuth.RemoteIdentity.Name); - Assert.True(serverAuth.IsAuthenticated, "IsAuthenticated"); Assert.True(serverAuth.IsEncrypted, "IsEncrypted"); Assert.True(serverAuth.IsSigned, "IsSigned"); Assert.Equal(mutualAuthenitcated, serverAuth.IsMutuallyAuthenticated); + Assert.Equal(expectedAuthenticationType, serverAuth.RemoteIdentity.AuthenticationType); + Assert.Equal(serverSPN, serverAuth.RemoteIdentity.Name); + // Send a message to the server. Encode the test data into a byte array. byte[] message = Encoding.UTF8.GetBytes("Hello from the server."); await serverAuth.WriteAsync(message, 0, message.Length); From 96f950049e7c2209485943f0b81cc95ca6aac4f7 Mon Sep 17 00:00:00 2001 From: Chris R Date: Fri, 12 Apr 2019 09:51:48 -0700 Subject: [PATCH 06/17] Get the user --- .../Interop.NetSecurityNative.cs | 6 +++ .../Net/Security/NegotiateStreamPal.Unix.cs | 54 +++++++++++++++++-- .../System.Net.Security.Native/pal_gssapi.c | 38 +++++++++++++ .../System.Net.Security.Native/pal_gssapi.h | 7 +++ .../Net/Security/NegotiateStreamPal.Unix.cs | 19 +++++-- .../NegotiateStreamKerberosTest.cs | 2 +- 6 files changed, 117 insertions(+), 9 deletions(-) diff --git a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs index b8f4e37c74e2..b3add795f2ef 100644 --- a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs +++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs @@ -98,6 +98,12 @@ internal static extern Status DeleteSecContext( out Status minorStatus, ref IntPtr contextHandle); + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_GetUser")] + internal static extern Status GetUser( + out Status minorStatus, + SafeGssContextHandle acceptContextHandle, + ref GssBuffer token); + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_Wrap")] private static extern Status Wrap( out Status minorStatus, diff --git a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 7e4bfe706fd6..247f7811a745 100644 --- a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -3,14 +3,15 @@ // See the LICENSE file in the project root for more information. using System.IO; -using System.Security; -using System.Security.Principal; -using System.Threading; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Security; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; +using System.Security.Principal; +using System.Text; +using System.Threading; using Microsoft.Win32.SafeHandles; namespace System.Net.Security @@ -206,6 +207,32 @@ private static bool GssAcceptSecurityContext( return status == Interop.NetSecurityNative.Status.GSS_S_COMPLETE; } + private static string GssGetUser( + ref SafeGssContextHandle context) + { + Interop.NetSecurityNative.GssBuffer token = default(Interop.NetSecurityNative.GssBuffer); + + try + { + + Interop.NetSecurityNative.Status minorStatus; + var status = Interop.NetSecurityNative.GetUser(out minorStatus, + context, + ref token); + + if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) + { + throw new Interop.NetSecurityNative.GssApiException(status, minorStatus); + } + + return Encoding.ASCII.GetString(token.ToByteArray()); + } + finally + { + token.Dispose(); + } + } + private static SecurityStatusPal EstablishSecurityContext( SafeFreeNegoCredentials credential, ref SafeDeleteContext context, @@ -407,6 +434,27 @@ internal static SecurityStatusPal AcceptSecurityContext( } } + internal static string GetUser( + ref SafeDeleteContext securityContext) + { + if (securityContext == null) + { + throw new ArgumentNullException(nameof(securityContext)); + } + + SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext; + try + { + SafeGssContextHandle contextHandle = negoContext.GssContext; + return GssGetUser(ref contextHandle); + } + catch (Exception ex) + { + if (NetEventSource.IsEnabled) NetEventSource.Error(null, ex); + throw; + } + } + internal static Win32Exception CreateExceptionFromError(SecurityStatusPal statusCode) { return new Win32Exception(NTE_FAIL, (statusCode.Exception != null) ? statusCode.Exception.Message : statusCode.ErrorCode.ToString()); diff --git a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c index 9ad90d84a238..926cd4e64f23 100644 --- a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c +++ b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.c @@ -275,6 +275,44 @@ uint32_t NetSecurityNative_AcceptSecContext(uint32_t* minorStatus, return majorStatus; } +uint32_t NetSecurityNative_GetUser(uint32_t* minorStatus, + GssCtxId* contextHandle, + PAL_GssBuffer* outBuffer) +{ + assert(minorStatus != NULL); + assert(contextHandle != NULL); + assert(outBuffer != NULL); + + gss_name_t srcName = GSS_C_NO_NAME; + + uint32_t majorStatus = gss_inquire_context(minorStatus, + contextHandle, + &srcName, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL); + + if (majorStatus == GSS_S_COMPLETE) + { + GssBuffer gssBuffer = {.length = 0, .value = NULL}; + majorStatus = gss_display_name(minorStatus, srcName, &gssBuffer, NULL); + if (majorStatus == GSS_S_COMPLETE) + { + NetSecurityNative_MoveBuffer(&gssBuffer, outBuffer); + } + } + + if (srcName != NULL) + { + majorStatus = gss_release_name(minorStatus, &srcName); + } + + return majorStatus; +} + uint32_t NetSecurityNative_ReleaseCred(uint32_t* minorStatus, GssCredId** credHandle) { assert(minorStatus != NULL); diff --git a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h index 8e3720669c7d..e3cf72fb4cc3 100644 --- a/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h +++ b/src/Native/Unix/System.Net.Security.Native/pal_gssapi.h @@ -168,3 +168,10 @@ DLLEXPORT uint32_t NetSecurityNative_InitiateCredWithPassword(uint32_t* minorSta Shims the gss_indicate_mechs method to detect if NTLM mech is installed. */ DLLEXPORT uint32_t NetSecurityNative_IsNtlmInstalled(void); + +/* +Shims gss_inquire_context and gss_display_name to get the remote user principal name. +*/ +DLLEXPORT uint32_t NetSecurityNative_GetUser(uint32_t* minorStatus, + GssCtxId* contextHandle, + PAL_GssBuffer* outBuffer); diff --git a/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 917bd3815328..6b8656dc06d1 100644 --- a/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -3,14 +3,15 @@ // See the LICENSE file in the project root for more information. using System.IO; -using System.Security; -using System.Security.Principal; -using System.Threading; using System.ComponentModel; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Security; using System.Security.Authentication; using System.Security.Authentication.ExtendedProtection; +using System.Security.Claims; +using System.Security.Principal; +using System.Threading; using Microsoft.Win32.SafeHandles; namespace System.Net.Security @@ -24,11 +25,19 @@ internal static partial class NegotiateStreamPal { internal static IIdentity GetIdentity(NTAuthentication context) { - if (context.IsServer) throw new PlatformNotSupportedException("GetIdentity: Server is not supported"); - string name = context.Spn; string protocol = context.ProtocolName; + if (context.IsServer) + { + var safeContext = context.GetContext(out var status); + if (status.ErrorCode != SecurityStatusPalErrorCode.OK) + { + throw new Exception(status.ErrorCode.ToString()); // TODO? + } + name = GetUser(ref safeContext); + } + return new GenericIdentity(name, protocol); } diff --git a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs index 65ab540e1d7f..dd8f034c1dba 100644 --- a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs @@ -332,7 +332,7 @@ private async Task VerifyRemoteServerAuthentication(NetworkCredential credential Assert.Equal(mutualAuthenitcated, serverAuth.IsMutuallyAuthenticated); Assert.Equal(expectedAuthenticationType, serverAuth.RemoteIdentity.AuthenticationType); - Assert.Equal(serverSPN, serverAuth.RemoteIdentity.Name); + Assert.Equal("Administrator@CRKERBEROS.COM", serverAuth.RemoteIdentity.Name); // Send a message to the server. Encode the test data into a byte array. byte[] message = Encoding.UTF8.GetBytes("Hello from the server."); From bb98da1484a7fa5cf885ae3a47d9405cf1640060 Mon Sep 17 00:00:00 2001 From: Chris R Date: Fri, 12 Apr 2019 10:37:54 -0700 Subject: [PATCH 07/17] Cleanup --- .../Net/Security/NegotiateStreamPal.Unix.cs | 48 ++----------------- 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 247f7811a745..4debbc336b85 100644 --- a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -172,7 +172,6 @@ private static bool GssAcceptSecurityContext( out byte[] outputBuffer, out uint outFlags) { - // do we need to initalize this? if (context == null) { context = new SafeGssContextHandle(); @@ -214,9 +213,7 @@ private static string GssGetUser( try { - - Interop.NetSecurityNative.Status minorStatus; - var status = Interop.NetSecurityNative.GetUser(out minorStatus, + var status = Interop.NetSecurityNative.GetUser(out var minorStatus, context, ref token); @@ -361,33 +358,11 @@ internal static SecurityStatusPal AcceptSecurityContext( ref byte[] resultBlob, ref ContextFlagsPal contextFlags) { - // bool isNtlmOnly = credential.IsNtlmOnly; - if (securityContext == null) { - /* - if (NetEventSource.IsEnabled) - { - string protocol = isNtlmOnly ? "NTLM" : "SPNEGO"; - NetEventSource.Info(null, $"requested protocol = {protocol}, target = {targetName}"); - } - */ securityContext = new SafeDeleteNegoContext((SafeFreeNegoCredentials)credentialsHandle); } - if (credentialsHandle == null) - { - throw new ArgumentNullException(nameof(credentialsHandle)); - } - if (securityContext == null) - { - throw new ArgumentNullException(nameof(securityContext)); - } - if (incomingBlob == null) - { - throw new ArgumentNullException(nameof(incomingBlob)); - } - SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext; try { @@ -397,19 +372,7 @@ internal static SecurityStatusPal AcceptSecurityContext( incomingBlob, out resultBlob, out uint outputFlags); -/* - if (done) - { - if (NetEventSource.IsEnabled) - { - string protocol = isNtlmOnly ? "NTLM" : isNtlmUsed ? "SPNEGO-NTLM" : "SPNEGO-Kerberos"; - NetEventSource.Info(null, $"actual protocol = {protocol}"); - } - // Populate protocol used for authentication - negoContext.SetAuthenticationPackage(isNtlmUsed); - } -*/ Debug.Assert(resultBlob != null, "Unexpected null buffer returned by GssApi"); Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext); @@ -422,9 +385,11 @@ internal static SecurityStatusPal AcceptSecurityContext( contextFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop( (Interop.NetSecurityNative.GssFlags)outputFlags, isServer: true); + SecurityStatusPalErrorCode errorCode = done ? (negoContext.IsNtlmUsed && resultBlob.Length > 0 ? SecurityStatusPalErrorCode.OK : SecurityStatusPalErrorCode.CompleteNeeded) : SecurityStatusPalErrorCode.ContinueNeeded; + return new SecurityStatusPal(errorCode); } catch (Exception ex) @@ -434,14 +399,9 @@ internal static SecurityStatusPal AcceptSecurityContext( } } - internal static string GetUser( + private static string GetUser( ref SafeDeleteContext securityContext) { - if (securityContext == null) - { - throw new ArgumentNullException(nameof(securityContext)); - } - SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext; try { From d6166fdda944a268b5d57d6665211ed1ae0a5cce Mon Sep 17 00:00:00 2001 From: Chris R Date: Fri, 12 Apr 2019 10:41:49 -0700 Subject: [PATCH 08/17] Disable test for CI --- .../tests/FunctionalTests/NegotiateStreamKerberosTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs index dd8f034c1dba..221e99fba71c 100644 --- a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs @@ -267,7 +267,7 @@ private async Task VerifyLocalServerAuthentication(NetworkCredential credential) } [OuterLoop] - [Theory] + // [Theory] // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] [MemberData(nameof(GoodCredentialsData))] public async Task NegotiateStream_RemoteServerAuthentication_Success(object credentialObject) From 7e9b14077948ea4bc0e1ec9b680d34192b367638 Mon Sep 17 00:00:00 2001 From: Chris R Date: Tue, 16 Apr 2019 14:50:50 -0700 Subject: [PATCH 09/17] Rewrite the tests --- .../System/Net/Configuration.Security.cs | 4 + .../NegotiateStreamKerberosTest.cs | 224 +++++------------- 2 files changed, 63 insertions(+), 165 deletions(-) diff --git a/src/Common/tests/System/Net/Configuration.Security.cs b/src/Common/tests/System/Net/Configuration.Security.cs index 91344ce760a7..b1e213bd8a6c 100644 --- a/src/Common/tests/System/Net/Configuration.Security.cs +++ b/src/Common/tests/System/Net/Configuration.Security.cs @@ -22,6 +22,10 @@ public static partial class Security public static Uri NegotiateServer => GetUriValue("COREFX_NET_SECURITY_NEGOSERVERURI"); + public static Uri NegotiateClient => GetUriValue("COREFX_NET_SECURITY_NEGOCLIENTURI"); + + public static string NegotiateClientUser => GetValue("COREFX_NET_SECURITY_NEGOCLIENTUSER"); + public static string TlsRenegotiationServer => GetValue("COREFX_NET_SECURITY_TLSREGOTIATIONSERVER", "corefx-net-tls.azurewebsites.net"); // This should be set if hostnames for all certificates within corefx-testdata have been set to point to 127.0.0.1. diff --git a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs index 221e99fba71c..a6ca3c00aa12 100644 --- a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs @@ -3,6 +3,8 @@ // See the LICENSE file in the project root for more information. using System.Collections.Generic; +using System.Globalization; +using System.IO; using System.Linq; using System.Net.Sockets; using System.Net.Test.Common; @@ -11,6 +13,7 @@ using System.Security.Authentication.ExtendedProtection; using System.Security.Principal; using System.Text; +using System.Threading; using System.Threading.Tasks; using Xunit; @@ -29,7 +32,6 @@ public static IEnumerable GoodCredentialsData get { yield return new object[] { CredentialCache.DefaultNetworkCredentials }; - /* yield return new object[] { new NetworkCredential( Configuration.Security.ActiveDirectoryUserName, Configuration.Security.ActiveDirectoryUserPassword, @@ -62,7 +64,6 @@ public static IEnumerable GoodCredentialsData Configuration.Security.ActiveDirectoryUserName, (SecureString)null, null) }; - */ } } @@ -94,7 +95,7 @@ public static IEnumerable BadCredentialsData } [OuterLoop] - // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] + [ConditionalTheory(nameof(IsServerAndDomainAvailable))] [MemberData(nameof(GoodCredentialsData))] public async Task NegotiateStream_ClientAuthenticationRemote_Success(object credentialObject) { @@ -103,7 +104,7 @@ public async Task NegotiateStream_ClientAuthenticationRemote_Success(object cred } [OuterLoop] - // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] + [ConditionalTheory(nameof(IsServerAndDomainAvailable))] [MemberData(nameof(BadCredentialsData))] public async Task NegotiateStream_ClientAuthenticationRemote_Fails(object credentialObject) { @@ -162,169 +163,29 @@ await auth.AuthenticateAsClientAsync( } [OuterLoop] - // [Theory] - // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] - [MemberData(nameof(GoodCredentialsData))] - public async Task NegotiateStream_LocalServerAuthentication_Success(object credentialObject) - { - var credential = (NetworkCredential)credentialObject; - await VerifyLocalServerAuthentication(credential); - } - - [OuterLoop] - // [Theory] - // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] - [MemberData(nameof(BadCredentialsData))] - public async Task NegotiateStream_LocalServerAuthentication_Fails(object credentialObject) - { - var credential = (NetworkCredential)credentialObject; - await Assert.ThrowsAsync(() => VerifyLocalServerAuthentication(credential)); - } - - private async Task VerifyLocalServerAuthentication(NetworkCredential credential) + // [Fact] + // [ConditionalFact(nameof(IsServerAndDomainAvailable))] + public async Task NegotiateStream_RemoteServerAuthentication_Success() { - string serverName = "chrross-udesk.crkerberos.com";// Configuration.Security.NegotiateServer.Host; - int port = 54321;// Configuration.Security.NegotiateServer.Port; - string serverSPN = "HTTP/" + serverName; + string expectedUser = Configuration.Security.NegotiateClientUser; string expectedAuthenticationType = "Kerberos"; bool mutualAuthenitcated = true; -/* - bool isLocalhost = false; // await IsLocalHost(serverName); - if (credential == CredentialCache.DefaultNetworkCredentials && isLocalhost) - { - expectedAuthenticationType = "NTLM"; - } - else if (credential != CredentialCache.DefaultNetworkCredentials && - (string.IsNullOrEmpty(credential.UserName) || string.IsNullOrEmpty(credential.Password))) - { - // Anonymous authentication. - expectedAuthenticationType = "NTLM"; - mutualAuthenitcated = false; - } -*/ - using (var client = new TcpClient()) - { - var server = new TcpListener(IPAddress.Parse("10.121.100.113"), port); - server.Start(); - try - { - var acceptTask = server.AcceptTcpClientAsync(); - await client.ConnectAsync(IPAddress.Parse("10.121.100.113"), port); - - var serverConnection = await acceptTask; - var serverStream = serverConnection.GetStream(); -/* - var loop = true; - while (loop) - { - await Task.Delay(1000); - } -*/ - using (var serverAuth = new NegotiateStream(serverStream, leaveInnerStreamOpen: false)) - { - var clientStream = client.GetStream(); - using (var clientAuth = new NegotiateStream(clientStream, leaveInnerStreamOpen:false)) - { - var serverAuthTask = serverAuth.AuthenticateAsServerAsync(); - var clientAuthTask = clientAuth.AuthenticateAsClientAsync( - credential, - serverSPN, - ProtectionLevel.None, - System.Security.Principal.TokenImpersonationLevel.Identification); - - await serverAuthTask; - - Assert.Equal(expectedAuthenticationType, serverAuth.RemoteIdentity.AuthenticationType); - Assert.Equal(serverSPN, serverAuth.RemoteIdentity.Name); - - Assert.Equal(true, serverAuth.IsAuthenticated); - Assert.Equal(true, serverAuth.IsEncrypted); - Assert.Equal(mutualAuthenitcated, serverAuth.IsMutuallyAuthenticated); - Assert.Equal(true, serverAuth.IsSigned); - - await clientAuthTask; - - Assert.Equal(expectedAuthenticationType, clientAuth.RemoteIdentity.AuthenticationType); - Assert.Equal(serverSPN, clientAuth.RemoteIdentity.Name); - - Assert.Equal(true, clientAuth.IsAuthenticated); - Assert.Equal(true, clientAuth.IsEncrypted); - Assert.Equal(mutualAuthenitcated, clientAuth.IsMutuallyAuthenticated); - Assert.Equal(true, clientAuth.IsSigned); - - // Send a message to the server. Encode the test data into a byte array. - byte[] message = Encoding.UTF8.GetBytes("Hello from the client."); - await clientAuth.WriteAsync(message, 0, message.Length); - } - } - } - finally - { - server.Stop(); - } - } - } - - [OuterLoop] - // [Theory] - // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] - [MemberData(nameof(GoodCredentialsData))] - public async Task NegotiateStream_RemoteServerAuthentication_Success(object credentialObject) - { - var credential = (NetworkCredential)credentialObject; - await VerifyRemoteServerAuthentication(credential); - } - [OuterLoop] - // [Theory] - // [ConditionalTheory(nameof(IsServerAndDomainAvailable))] - [MemberData(nameof(BadCredentialsData))] - public async Task NegotiateStream_RemoteServerAuthentication_Fails(object credentialObject) - { - var credential = (NetworkCredential)credentialObject; - await Assert.ThrowsAsync(() => VerifyRemoteServerAuthentication(credential)); - } - - private async Task VerifyRemoteServerAuthentication(NetworkCredential credential) - { - string serverName = "chrross-udesk.crkerberos.com";// Configuration.Security.NegotiateServer.Host; - int port = 54321;// Configuration.Security.NegotiateServer.Port; - string serverSPN = "HTTP/" + serverName; - - string expectedAuthenticationType = "Kerberos"; - bool mutualAuthenitcated = true; -/* - if (credential != CredentialCache.DefaultNetworkCredentials && - (string.IsNullOrEmpty(credential.UserName) || string.IsNullOrEmpty(credential.Password))) + using (var controlClient = new TcpClient()) { - // Anonymous authentication. - expectedAuthenticationType = "NTLM"; - mutualAuthenitcated = false; - } -*/ - var server = new TcpListener(IPAddress.Parse("10.121.100.113"), port); - server.Start(); - try - { - var acceptTask = server.AcceptTcpClientAsync(); - // TODO: Signal client to connect - - var serverConnection = await acceptTask; - var serverStream = serverConnection.GetStream(); -/* - var loop = true; - while (loop) - { - await Task.Delay(1000); - } -*/ + string clientName = Configuration.Security.NegotiateClient.Host; + int clientPort = Configuration.Security.NegotiateClient.Port; + await controlClient.ConnectAsync(clientName, clientPort).DefaultTimeout(); + var serverStream = controlClient.GetStream(); + using (var serverAuth = new NegotiateStream(serverStream, leaveInnerStreamOpen: false)) { - serverAuth.AuthenticateAsServer( + await serverAuth.AuthenticateAsServerAsync( CredentialCache.DefaultNetworkCredentials, ProtectionLevel.EncryptAndSign, - TokenImpersonationLevel.Identification); + TokenImpersonationLevel.Identification) + .DefaultTimeout(); Assert.True(serverAuth.IsAuthenticated, "IsAuthenticated"); Assert.True(serverAuth.IsEncrypted, "IsEncrypted"); @@ -332,17 +193,17 @@ private async Task VerifyRemoteServerAuthentication(NetworkCredential credential Assert.Equal(mutualAuthenitcated, serverAuth.IsMutuallyAuthenticated); Assert.Equal(expectedAuthenticationType, serverAuth.RemoteIdentity.AuthenticationType); - Assert.Equal("Administrator@CRKERBEROS.COM", serverAuth.RemoteIdentity.Name); + Assert.Equal(expectedUser, serverAuth.RemoteIdentity.Name); - // Send a message to the server. Encode the test data into a byte array. - byte[] message = Encoding.UTF8.GetBytes("Hello from the server."); - await serverAuth.WriteAsync(message, 0, message.Length); + // Receive a message from the client. + var message = "Hello from the client."; + using (var reader = new StreamReader(serverAuth)) + { + var response = await reader.ReadToEndAsync().DefaultTimeout(); + Assert.Equal(message, response); + } } } - finally - { - server.Stop(); - } } private async static Task IsLocalHost(string hostname) @@ -375,4 +236,37 @@ private static SecureString AsSecureString(string str) return secureString; } } + + public static class TaskExtensions + { + public static async Task DefaultTimeout(this Task task) + { + var timeout = TimeSpan.FromSeconds(10); + var cts = new CancellationTokenSource(); + if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token))) + { + cts.Cancel(); + await task; + } + else + { + throw new TimeoutException(); + } + } + + public static async Task DefaultTimeout(this Task task) + { + var timeout = TimeSpan.FromSeconds(10); + var cts = new CancellationTokenSource(); + if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token))) + { + cts.Cancel(); + return await task; + } + else + { + throw new TimeoutException(); + } + } + } } From fd5657e235f967de7952fc0a888e40e3f8dbba1e Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 17 Apr 2019 09:20:56 -0700 Subject: [PATCH 10/17] TimeoutAfter --- .../Threading/Tasks/TaskTimeoutExtensions.cs | 18 +++++--- .../NegotiateStreamKerberosTest.cs | 41 ++----------------- 2 files changed, 16 insertions(+), 43 deletions(-) diff --git a/src/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs b/src/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs index c4c15187a8cd..734b57e9544c 100644 --- a/src/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs +++ b/src/Common/tests/System/Threading/Tasks/TaskTimeoutExtensions.cs @@ -25,33 +25,39 @@ public static async Task WithCancellation(this Task task, CancellationToken canc } } - public static async Task TimeoutAfter(this Task task, int millisecondsTimeout) + public static Task TimeoutAfter(this Task task, int millisecondsTimeout) + => task.TimeoutAfter(TimeSpan.FromMilliseconds(millisecondsTimeout)); + + public static async Task TimeoutAfter(this Task task, TimeSpan timeout) { var cts = new CancellationTokenSource(); - if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout, cts.Token)).ConfigureAwait(false)) + if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token)).ConfigureAwait(false)) { cts.Cancel(); await task.ConfigureAwait(false); } else { - throw new TimeoutException($"Task timed out after {millisecondsTimeout}ms"); + throw new TimeoutException($"Task timed out after {timeout}"); } } - public static async Task TimeoutAfter(this Task task, int millisecondsTimeout) + public static Task TimeoutAfter(this Task task, int millisecondsTimeout) + => task.TimeoutAfter(TimeSpan.FromMilliseconds(millisecondsTimeout)); + + public static async Task TimeoutAfter(this Task task, TimeSpan timeout) { var cts = new CancellationTokenSource(); - if (task == await Task.WhenAny(task, Task.Delay(millisecondsTimeout, cts.Token)).ConfigureAwait(false)) + if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token)).ConfigureAwait(false)) { cts.Cancel(); return await task.ConfigureAwait(false); } else { - throw new TimeoutException($"Task timed out after {millisecondsTimeout}ms"); + throw new TimeoutException($"Task timed out after {timeout}"); } } diff --git a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs index a6ca3c00aa12..31b4201afd41 100644 --- a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs @@ -10,7 +10,6 @@ using System.Net.Test.Common; using System.Security; using System.Security.Authentication; -using System.Security.Authentication.ExtendedProtection; using System.Security.Principal; using System.Text; using System.Threading; @@ -176,7 +175,8 @@ public async Task NegotiateStream_RemoteServerAuthentication_Success() { string clientName = Configuration.Security.NegotiateClient.Host; int clientPort = Configuration.Security.NegotiateClient.Port; - await controlClient.ConnectAsync(clientName, clientPort).DefaultTimeout(); + await controlClient.ConnectAsync(clientName, clientPort) + .TimeoutAfter(TimeSpan.FromSeconds(15)); var serverStream = controlClient.GetStream(); using (var serverAuth = new NegotiateStream(serverStream, leaveInnerStreamOpen: false)) @@ -185,7 +185,7 @@ await serverAuth.AuthenticateAsServerAsync( CredentialCache.DefaultNetworkCredentials, ProtectionLevel.EncryptAndSign, TokenImpersonationLevel.Identification) - .DefaultTimeout(); + .TimeoutAfter(TimeSpan.FromSeconds(15)); Assert.True(serverAuth.IsAuthenticated, "IsAuthenticated"); Assert.True(serverAuth.IsEncrypted, "IsEncrypted"); @@ -199,7 +199,7 @@ await serverAuth.AuthenticateAsServerAsync( var message = "Hello from the client."; using (var reader = new StreamReader(serverAuth)) { - var response = await reader.ReadToEndAsync().DefaultTimeout(); + var response = await reader.ReadToEndAsync().TimeoutAfter(TimeSpan.FromSeconds(15)); Assert.Equal(message, response); } } @@ -236,37 +236,4 @@ private static SecureString AsSecureString(string str) return secureString; } } - - public static class TaskExtensions - { - public static async Task DefaultTimeout(this Task task) - { - var timeout = TimeSpan.FromSeconds(10); - var cts = new CancellationTokenSource(); - if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token))) - { - cts.Cancel(); - await task; - } - else - { - throw new TimeoutException(); - } - } - - public static async Task DefaultTimeout(this Task task) - { - var timeout = TimeSpan.FromSeconds(10); - var cts = new CancellationTokenSource(); - if (task == await Task.WhenAny(task, Task.Delay(timeout, cts.Token))) - { - cts.Cancel(); - return await task; - } - else - { - throw new TimeoutException(); - } - } - } } From e09ee836c26c4465827662c155d73d24c856d5e6 Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 17 Apr 2019 09:28:18 -0700 Subject: [PATCH 11/17] Real skip condition --- src/Common/tests/System/Net/Capability.Security.cs | 6 ++++++ .../tests/FunctionalTests/NegotiateStreamKerberosTest.cs | 8 +++++--- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/Common/tests/System/Net/Capability.Security.cs b/src/Common/tests/System/Net/Capability.Security.cs index 5280f554731b..009a9cd9c377 100644 --- a/src/Common/tests/System/Net/Capability.Security.cs +++ b/src/Common/tests/System/Net/Capability.Security.cs @@ -27,6 +27,12 @@ public static bool IsDomainAvailable() return !string.IsNullOrWhiteSpace(Configuration.Security.ActiveDirectoryName); } + public static bool IsNegotiateClientAvailable() + { + return !(Configuration.Security.NegotiateClient == null) + && !(Configuration.Security.NegotiateClientUser == null); + } + public static bool IsNegotiateServerAvailable() { return !(Configuration.Security.NegotiateServer == null); diff --git a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs index 31b4201afd41..0fd06c5c7a39 100644 --- a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs @@ -25,6 +25,9 @@ public class NegotiateStreamKerberosTest { public static bool IsServerAndDomainAvailable => Capability.IsDomainAvailable() && Capability.IsNegotiateServerAvailable(); + + public static bool IsClientAvailable => + Capability.IsNegotiateClientAvailable(); public static IEnumerable GoodCredentialsData { @@ -162,9 +165,8 @@ await auth.AuthenticateAsClientAsync( } [OuterLoop] - // [Fact] - // [ConditionalFact(nameof(IsServerAndDomainAvailable))] - public async Task NegotiateStream_RemoteServerAuthentication_Success() + [ConditionalFact(nameof(IsClientAvailable))] + public async Task NegotiateStream_ServerAuthenticationRemote_Success() { string expectedUser = Configuration.Security.NegotiateClientUser; From 9285c5bad0c21e34c62a0d702a3ecd60630e04f3 Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 17 Apr 2019 10:06:40 -0700 Subject: [PATCH 12/17] Throw Win32Exception --- .../src/System/Net/Security/NegotiateStreamPal.Unix.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 6b8656dc06d1..ab7a245e4e16 100644 --- a/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/System.Net.Security/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -33,7 +33,7 @@ internal static IIdentity GetIdentity(NTAuthentication context) var safeContext = context.GetContext(out var status); if (status.ErrorCode != SecurityStatusPalErrorCode.OK) { - throw new Exception(status.ErrorCode.ToString()); // TODO? + throw new Win32Exception((int)status.ErrorCode); } name = GetUser(ref safeContext); } From d542839aa87e4acbcf8743cd0590214a522a30f7 Mon Sep 17 00:00:00 2001 From: Chris R Date: Wed, 17 Apr 2019 14:42:44 -0700 Subject: [PATCH 13/17] UTF8 users, spelling --- .../src/System/Net/Security/NegotiateStreamPal.Unix.cs | 2 +- .../FunctionalTests/NegotiateStreamKerberosTest.cs | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 4debbc336b85..76f947d7c99c 100644 --- a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -222,7 +222,7 @@ private static string GssGetUser( throw new Interop.NetSecurityNative.GssApiException(status, minorStatus); } - return Encoding.ASCII.GetString(token.ToByteArray()); + return Encoding.UTF8.GetString(token.ToByteArray()); } finally { diff --git a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs index 0fd06c5c7a39..23f31e354270 100644 --- a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamKerberosTest.cs @@ -122,7 +122,7 @@ private async Task VerifyClientAuthentication(NetworkCredential credential) bool isLocalhost = await IsLocalHost(serverName); string expectedAuthenticationType = "Kerberos"; - bool mutualAuthenitcated = true; + bool mutuallyAuthenticated = true; if (credential == CredentialCache.DefaultNetworkCredentials && isLocalhost) { @@ -133,7 +133,7 @@ private async Task VerifyClientAuthentication(NetworkCredential credential) { // Anonymous authentication. expectedAuthenticationType = "NTLM"; - mutualAuthenitcated = false; + mutuallyAuthenticated = false; } using (var client = new TcpClient()) @@ -154,7 +154,7 @@ await auth.AuthenticateAsClientAsync( Assert.Equal(true, auth.IsAuthenticated); Assert.Equal(true, auth.IsEncrypted); - Assert.Equal(mutualAuthenitcated, auth.IsMutuallyAuthenticated); + Assert.Equal(mutuallyAuthenticated, auth.IsMutuallyAuthenticated); Assert.Equal(true, auth.IsSigned); // Send a message to the server. Encode the test data into a byte array. @@ -171,7 +171,7 @@ public async Task NegotiateStream_ServerAuthenticationRemote_Success() string expectedUser = Configuration.Security.NegotiateClientUser; string expectedAuthenticationType = "Kerberos"; - bool mutualAuthenitcated = true; + bool mutuallyAuthenticated = true; using (var controlClient = new TcpClient()) { @@ -192,7 +192,7 @@ await serverAuth.AuthenticateAsServerAsync( Assert.True(serverAuth.IsAuthenticated, "IsAuthenticated"); Assert.True(serverAuth.IsEncrypted, "IsEncrypted"); Assert.True(serverAuth.IsSigned, "IsSigned"); - Assert.Equal(mutualAuthenitcated, serverAuth.IsMutuallyAuthenticated); + Assert.Equal(mutuallyAuthenticated, serverAuth.IsMutuallyAuthenticated); Assert.Equal(expectedAuthenticationType, serverAuth.RemoteIdentity.AuthenticationType); Assert.Equal(expectedUser, serverAuth.RemoteIdentity.Name); From 84adb5c9ec33138cac2a1a62d5ffa569794d53d3 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 18 Apr 2019 09:08:58 -0700 Subject: [PATCH 14/17] IsServer check --- src/Common/src/System/Net/NTAuthentication.Common.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Common/src/System/Net/NTAuthentication.Common.cs b/src/Common/src/System/Net/NTAuthentication.Common.cs index 20a4f40da931..f47cb85b7d8f 100644 --- a/src/Common/src/System/Net/NTAuthentication.Common.cs +++ b/src/Common/src/System/Net/NTAuthentication.Common.cs @@ -293,7 +293,7 @@ internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out Secu // The return value will tell us correctly if the handshake is over or not if (statusCode.ErrorCode == SecurityStatusPalErrorCode.OK - || statusCode.ErrorCode == SecurityStatusPalErrorCode.CompleteNeeded) // Server only + || (_isServer && statusCode.ErrorCode == SecurityStatusPalErrorCode.CompleteNeeded)) { // Success. _isCompleted = true; From 8d774be374f249b1dadff470aa69dcd166c1fa75 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 18 Apr 2019 10:16:42 -0700 Subject: [PATCH 15/17] Dispose, nonvar --- .../Net/Security/NegotiateStreamPal.Unix.cs | 21 ++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs index 76f947d7c99c..1b17c6f15cc6 100644 --- a/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs +++ b/src/Common/src/System/Net/Security/NegotiateStreamPal.Unix.cs @@ -125,8 +125,10 @@ private static bool GssInitSecurityContext( // EstablishSecurityContext is called multiple times in a session. // In each call, we need to pass the context handle from the previous call. // For the first call, the context handle will be null. + bool newContext = false; if (context == null) { + newContext = true; context = new SafeGssContextHandle(); } @@ -153,6 +155,11 @@ private static bool GssInitSecurityContext( if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) && (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED)) { + if (newContext) + { + context.Dispose(); + context = null; + } throw new Interop.NetSecurityNative.GssApiException(status, minorStatus); } @@ -172,8 +179,10 @@ private static bool GssAcceptSecurityContext( out byte[] outputBuffer, out uint outFlags) { + bool newContext = false; if (context == null) { + newContext = true; context = new SafeGssContextHandle(); } @@ -193,6 +202,11 @@ private static bool GssAcceptSecurityContext( if ((status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) && (status != Interop.NetSecurityNative.Status.GSS_S_CONTINUE_NEEDED)) { + if (newContext) + { + context.Dispose(); + context = null; + } throw new Interop.NetSecurityNative.GssApiException(status, minorStatus); } @@ -213,9 +227,10 @@ private static string GssGetUser( try { - var status = Interop.NetSecurityNative.GetUser(out var minorStatus, - context, - ref token); + Interop.NetSecurityNative.Status status + = Interop.NetSecurityNative.GetUser(out var minorStatus, + context, + ref token); if (status != Interop.NetSecurityNative.Status.GSS_S_COMPLETE) { From 48a888a2cb092049155cca3386225f642b5ea8e4 Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 18 Apr 2019 10:18:02 -0700 Subject: [PATCH 16/17] IsNtlmInstalled --- .../NegotiateStreamStreamToStreamTest.cs | 22 ++++++++++--------- .../System.Net.Security.Tests.csproj | 12 +++++++--- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamStreamToStreamTest.cs b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamStreamToStreamTest.cs index 20d21fa375a9..05ad57572dae 100644 --- a/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamStreamToStreamTest.cs +++ b/src/System.Net.Security/tests/FunctionalTests/NegotiateStreamStreamToStreamTest.cs @@ -13,9 +13,11 @@ namespace System.Net.Security.Tests { - [PlatformSpecific(TestPlatforms.Windows)] // NegotiateStream only supports client-side functionality on Unix + [PlatformSpecific(TestPlatforms.Windows)] // NegotiateStream client needs explicit credentials or SPNs on unix. public abstract class NegotiateStreamStreamToStreamTest { + public static bool IsNtlmInstalled => Capability.IsNtlmInstalled(); + private const int PartialBytesToRead = 5; private static readonly byte[] s_sampleMsg = Encoding.UTF8.GetBytes("Sample Test Message"); @@ -26,7 +28,7 @@ public abstract class NegotiateStreamStreamToStreamTest protected abstract Task AuthenticateAsClientAsync(NegotiateStream client, NetworkCredential credential, string targetName); protected abstract Task AuthenticateAsServerAsync(NegotiateStream server); - [Fact] + [ConditionalFact(nameof(IsNtlmInstalled))] public async Task NegotiateStream_StreamToStream_Authentication_Success() { VirtualNetwork network = new VirtualNetwork(); @@ -76,7 +78,7 @@ public async Task NegotiateStream_StreamToStream_Authentication_Success() } } - [Fact] + [ConditionalFact(nameof(IsNtlmInstalled))] public async Task NegotiateStream_StreamToStream_Authentication_TargetName_Success() { string targetName = "testTargetName"; @@ -132,7 +134,7 @@ public async Task NegotiateStream_StreamToStream_Authentication_TargetName_Succe } } - [Fact] + [ConditionalFact(nameof(IsNtlmInstalled))] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, ".NET Core difference in behavior: https://github.com/dotnet/corefx/issues/5241")] public async Task NegotiateStream_StreamToStream_Authentication_EmptyCredentials_Fails() { @@ -195,7 +197,7 @@ public async Task NegotiateStream_StreamToStream_Authentication_EmptyCredentials } } - [Fact] + [ConditionalFact(nameof(IsNtlmInstalled))] public async Task NegotiateStream_StreamToStream_Successive_ClientWrite_Sync_Success() { byte[] recvBuf = new byte[s_sampleMsg.Length]; @@ -234,7 +236,7 @@ public async Task NegotiateStream_StreamToStream_Successive_ClientWrite_Sync_Suc } } - [Fact] + [ConditionalFact(nameof(IsNtlmInstalled))] public async Task NegotiateStream_StreamToStream_Successive_ClientWrite_Async_Success() { byte[] recvBuf = new byte[s_sampleMsg.Length]; @@ -273,7 +275,7 @@ public async Task NegotiateStream_StreamToStream_Successive_ClientWrite_Async_Su } } - [Fact] + [ConditionalFact(nameof(IsNtlmInstalled))] public async Task NegotiateStream_ReadWriteLongMsgSync_Success() { byte[] recvBuf = new byte[s_longMsg.Length]; @@ -300,7 +302,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( } } - [Fact] + [ConditionalFact(nameof(IsNtlmInstalled))] public async Task NegotiateStream_ReadWriteLongMsgAsync_Success() { byte[] recvBuf = new byte[s_longMsg.Length]; @@ -327,7 +329,7 @@ await TestConfiguration.WhenAllOrAnyFailedWithTimeout( } } - [Fact] + [ConditionalFact(nameof(IsNtlmInstalled))] public void NegotiateStream_StreamToStream_Flush_Propagated() { VirtualNetwork network = new VirtualNetwork(); @@ -341,7 +343,7 @@ public void NegotiateStream_StreamToStream_Flush_Propagated() } } - [Fact] + [ConditionalFact(nameof(IsNtlmInstalled))] [SkipOnTargetFramework(TargetFrameworkMonikers.NetFramework, "Relies on FlushAsync override not available in desktop")] public void NegotiateStream_StreamToStream_FlushAsync_Propagated() { diff --git a/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index 5e2b1dc4478b..731d57c63e6b 100644 --- a/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -110,6 +110,15 @@ + + Common\System\Net\Capability.Security.Unix.cs + + + Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurityNative.IsNtlmInstalled.cs + + + Common\Interop\Unix\Interop.Libraries.cs + @@ -130,9 +139,6 @@ - - Common\Interop\Unix\Interop.Libraries.cs - Common\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs From 0b0ea1fa8b6bb6cc64e59bcfca40b65c123306ce Mon Sep 17 00:00:00 2001 From: Chris R Date: Thu, 18 Apr 2019 10:37:38 -0700 Subject: [PATCH 17/17] IsNtlmInstalled for Windows --- .../tests/FunctionalTests/System.Net.Security.Tests.csproj | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj b/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj index 731d57c63e6b..28c2f8178700 100644 --- a/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj +++ b/src/System.Net.Security/tests/FunctionalTests/System.Net.Security.Tests.csproj @@ -107,6 +107,9 @@ + + Common\System\Net\Capability.Security.Windows.cs +