Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,12 @@ private static async Task<HttpResponseMessage> SendWithNtAuthAsync(HttpRequestMe
needDrain = true;
}
}
catch (PlatformNotSupportedException)
{
// Ignore PNSE from NegotiateAuthentication with unsupported parameters
// and treat it the same way as if we didn't support the authentication
// in the first place.
}
finally
{
if (isNewConnection)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,5 +139,33 @@ await server.AcceptConnectionAsync(async connection =>
}).ConfigureAwait(false);
});
}

[Fact]
[SkipOnPlatform(TestPlatforms.Browser | TestPlatforms.Windows, "DefaultCredentials are unsupported for NTLM on Unix / Managed implementation")]
public async Task DefaultHandler_FakeServer_DefaultCredentials()
{
await LoopbackServer.CreateClientAndServerAsync(
async uri =>
{
HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, uri);
requestMessage.Version = new Version(1, 1);

HttpMessageHandler handler = new HttpClientHandler() { Credentials = CredentialCache.DefaultCredentials };
using (var client = new HttpClient(handler))
{
HttpResponseMessage response = await client.SendAsync(requestMessage);
Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
}
},
async server =>
{
await server.AcceptConnectionAsync(async connection =>
{
var authHeader = "WWW-Authenticate: NTLM\r\n";
await connection.SendResponseAsync(HttpStatusCode.Unauthorized, authHeader).ConfigureAwait(false);
connection.CompleteRequestProcessing();
}).ConfigureAwait(false);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -222,13 +222,16 @@ public ManagedNtlmNegotiateAuthenticationPal(NegotiateAuthenticationClientOption
{
Debug.Assert(clientOptions.Package == NegotiationInfoClass.NTLM);

_credential = clientOptions.Credential;
if (string.IsNullOrWhiteSpace(_credential.UserName) || string.IsNullOrWhiteSpace(_credential.Password))
if (clientOptions.Credential == CredentialCache.DefaultNetworkCredentials ||
string.IsNullOrWhiteSpace(clientOptions.Credential.UserName) ||
string.IsNullOrWhiteSpace(clientOptions.Credential.Password))
{
// NTLM authentication is not possible with default credentials which are no-op
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(null, SR.net_ntlm_not_possible_default_cred);
throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred);
}

_credential = clientOptions.Credential;
_spn = clientOptions.TargetName;
_channelBinding = clientOptions.Binding;
_protectionLevel = clientOptions.RequiredProtectionLevel;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ public override void Dispose()

private NegotiateAuthenticationPal CreateMechanismForPackage(string packageName)
{
return NegotiateAuthenticationPal.Create(new NegotiateAuthenticationClientOptions
var updatedClientOptions = new NegotiateAuthenticationClientOptions
{
Package = packageName,
Credential = _clientOptions.Credential,
Expand All @@ -124,7 +124,16 @@ private NegotiateAuthenticationPal CreateMechanismForPackage(string packageName)
RequiredProtectionLevel = _clientOptions.RequiredProtectionLevel,
RequireMutualAuthentication = _clientOptions.RequireMutualAuthentication,
AllowedImpersonationLevel = _clientOptions.AllowedImpersonationLevel,
});
};

try
{
return NegotiateAuthenticationPal.Create(updatedClientOptions);
}
catch (PlatformNotSupportedException)
{
return new UnsupportedNegotiateAuthenticationPal(updatedClientOptions);
}
}

private IEnumerable<KeyValuePair<string, string>> EnumerateMechanisms()
Expand Down Expand Up @@ -303,16 +312,7 @@ private IEnumerable<KeyValuePair<string, string>> EnumerateMechanisms()
{
// Abandon the optimistic path and restart with a new mechanism
_optimisticMechanism?.Dispose();
_mechanism = NegotiateAuthenticationPal.Create(new NegotiateAuthenticationClientOptions
{
Package = requestedPackage,
Credential = _clientOptions.Credential,
TargetName = _clientOptions.TargetName,
Binding = _clientOptions.Binding,
RequiredProtectionLevel = _clientOptions.RequiredProtectionLevel,
RequireMutualAuthentication = _clientOptions.RequireMutualAuthentication,
AllowedImpersonationLevel = _clientOptions.AllowedImpersonationLevel,
});
_mechanism = CreateMechanismForPackage(requestedPackage);
}

_optimisticMechanism = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,15 @@ public static NegotiateAuthenticationPal Create(NegotiateAuthenticationClientOpt
{
return new UnixNegotiateAuthenticationPal(clientOptions);
}
catch (Win32Exception)
catch (Interop.NetSecurityNative.GssApiException gex)
{
return new UnsupportedNegotiateAuthenticationPal(clientOptions);
}
catch (PlatformNotSupportedException)
{
return new UnsupportedNegotiateAuthenticationPal(clientOptions);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
NegotiateAuthenticationStatusCode statusCode = UnixNegotiateAuthenticationPal.GetErrorCode(gex);
if (statusCode <= NegotiateAuthenticationStatusCode.GenericFailure)
{
statusCode = NegotiateAuthenticationStatusCode.Unsupported;
}
return new UnsupportedNegotiateAuthenticationPal(clientOptions, statusCode);
}
catch (EntryPointNotFoundException)
{
Expand All @@ -63,13 +65,15 @@ public static NegotiateAuthenticationPal Create(NegotiateAuthenticationServerOpt
{
return new UnixNegotiateAuthenticationPal(serverOptions);
}
catch (Win32Exception)
catch (Interop.NetSecurityNative.GssApiException gex)
{
return new UnsupportedNegotiateAuthenticationPal(serverOptions);
}
catch (PlatformNotSupportedException)
{
return new UnsupportedNegotiateAuthenticationPal(serverOptions);
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(null, gex);
NegotiateAuthenticationStatusCode statusCode = UnixNegotiateAuthenticationPal.GetErrorCode(gex);
if (statusCode <= NegotiateAuthenticationStatusCode.GenericFailure)
{
statusCode = NegotiateAuthenticationStatusCode.Unsupported;
}
return new UnsupportedNegotiateAuthenticationPal(serverOptions, statusCode);
}
catch (EntryPointNotFoundException)
{
Expand Down Expand Up @@ -184,22 +188,25 @@ public UnixNegotiateAuthenticationPal(NegotiateAuthenticationClientOptions clien

if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Peer SPN-> '{_spn}'");

if (clientOptions.Credential == CredentialCache.DefaultCredentials ||
if (clientOptions.Credential == CredentialCache.DefaultNetworkCredentials ||
Copy link
Member Author

@filipnavara filipnavara Aug 25, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Note: The equality check never worked because the objects are of different type.

string.IsNullOrWhiteSpace(clientOptions.Credential.UserName) ||
string.IsNullOrWhiteSpace(clientOptions.Credential.Password))
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, "using DefaultCredentials");
_credentialsHandle = AcquireDefaultCredential();

if (_packageType == Interop.NetSecurityNative.PackageType.NTLM)
{
// NTLM authentication is not possible with default credentials which are no-op
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, SR.net_ntlm_not_possible_default_cred);
throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred);
}
if (string.IsNullOrEmpty(_spn))
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, SR.net_nego_not_supported_empty_target_with_defaultcreds);
throw new PlatformNotSupportedException(SR.net_nego_not_supported_empty_target_with_defaultcreds);
}

_credentialsHandle = SafeGssCredHandle.Create(string.Empty, string.Empty, _packageType);
}
else
{
Expand Down Expand Up @@ -229,7 +236,7 @@ public UnixNegotiateAuthenticationPal(NegotiateAuthenticationServerOptions serve

if (NetEventSource.Log.IsEnabled()) NetEventSource.Info(this, $"Peer SPN-> '{_spn}'");

if (serverOptions.Credential == CredentialCache.DefaultCredentials ||
if (serverOptions.Credential == CredentialCache.DefaultNetworkCredentials ||
string.IsNullOrWhiteSpace(serverOptions.Credential.UserName) ||
string.IsNullOrWhiteSpace(serverOptions.Credential.Password))
{
Expand Down Expand Up @@ -462,24 +469,7 @@ private static Interop.NetSecurityNative.PackageType GetPackageType(string packa
else
{
// Native shim currently supports only NTLM, Negotiate and Kerberos
throw new PlatformNotSupportedException(SR.net_securitypackagesupport);
}
}

private SafeGssCredHandle AcquireDefaultCredential()
{
try
{
return SafeGssCredHandle.Create(string.Empty, string.Empty, _packageType);
}
catch (Exception ex)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, ex);

// NOTE: We throw PlatformNotSupportedException which is caught in
// NegotiateAuthenticationPal.Create and transformed into instantiation of
// UnsupportedNegotiateAuthenticationPal.
throw new PlatformNotSupportedException(ex.Message, ex);
throw new Interop.NetSecurityNative.GssApiException(Interop.NetSecurityNative.Status.GSS_S_UNAVAILABLE, 0);
}
}

Expand Down Expand Up @@ -511,14 +501,10 @@ private SafeGssCredHandle AcquireCredentialsHandle(NetworkCredential credential)

return SafeGssCredHandle.Create(username, password, _packageType);
}
catch (Exception ex)
catch (Exception ex) when (ex is not Interop.NetSecurityNative.GssApiException)
{
if (NetEventSource.Log.IsEnabled()) NetEventSource.Error(this, ex);

// NOTE: We throw PlatformNotSupportedException which is caught in
// NegotiateAuthenticationPal.Create and transformed into instantiation of
// UnsupportedNegotiateAuthenticationPal.
throw new PlatformNotSupportedException(ex.Message, ex);
throw new Interop.NetSecurityNative.GssApiException(Interop.NetSecurityNative.Status.GSS_S_BAD_NAME, 0);
}
}

Expand Down Expand Up @@ -753,7 +739,7 @@ private NegotiateAuthenticationStatusCode AcceptSecurityContext(
}

// https://www.gnu.org/software/gss/reference/gss.pdf (page 25)
private static NegotiateAuthenticationStatusCode GetErrorCode(Interop.NetSecurityNative.GssApiException exception)
internal static NegotiateAuthenticationStatusCode GetErrorCode(Interop.NetSecurityNative.GssApiException exception)
{
switch (exception.MajorStatus)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ internal sealed class UnsupportedNegotiateAuthenticationPal : NegotiateAuthentic
{
private string _package;
private string? _targetName;
private NegotiateAuthenticationStatusCode _statusCode;

public override bool IsAuthenticated => false;
public override bool IsSigned => false;
Expand All @@ -25,15 +26,17 @@ internal sealed class UnsupportedNegotiateAuthenticationPal : NegotiateAuthentic
public override IIdentity RemoteIdentity => throw new InvalidOperationException();
public override System.Security.Principal.TokenImpersonationLevel ImpersonationLevel => System.Security.Principal.TokenImpersonationLevel.Impersonation;

public UnsupportedNegotiateAuthenticationPal(NegotiateAuthenticationClientOptions clientOptions)
public UnsupportedNegotiateAuthenticationPal(NegotiateAuthenticationClientOptions clientOptions, NegotiateAuthenticationStatusCode statusCode = NegotiateAuthenticationStatusCode.Unsupported)
{
_package = clientOptions.Package;
_targetName = clientOptions.TargetName;
_statusCode = statusCode;
}

public UnsupportedNegotiateAuthenticationPal(NegotiateAuthenticationServerOptions serverOptions)
public UnsupportedNegotiateAuthenticationPal(NegotiateAuthenticationServerOptions serverOptions, NegotiateAuthenticationStatusCode statusCode = NegotiateAuthenticationStatusCode.Unsupported)
{
_package = serverOptions.Package;
_statusCode = statusCode;
}

public override void Dispose()
Expand All @@ -42,7 +45,7 @@ public override void Dispose()

public override byte[]? GetOutgoingBlob(ReadOnlySpan<byte> incomingBlob, out NegotiateAuthenticationStatusCode statusCode)
{
statusCode = NegotiateAuthenticationStatusCode.Unsupported;
statusCode = _statusCode;
return null;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ public void Package_Unsupported_NTLM()
Assert.Equal(NegotiateAuthenticationStatusCode.Unsupported, statusCode);
}

[Fact]
[SkipOnPlatform(TestPlatforms.Windows, "The test is specific to GSSAPI / Managed implementations of NegotiateAuthentication")]
public void DefaultNetworkCredentials_NTLM_Throws()
{
NegotiateAuthenticationClientOptions clientOptions = new NegotiateAuthenticationClientOptions { Package = "NTLM", Credential = CredentialCache.DefaultNetworkCredentials, TargetName = "HTTP/foo" };
Assert.Throws<PlatformNotSupportedException>(() => new NegotiateAuthentication(clientOptions));
}

[Fact]
public void NtlmProtocolExampleTest()
{
Expand Down