-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Expose a high level NTAuth Negotiate API #29270
Comments
Those APIs need more |
The latest changes to the System.Net.Security.Native shim layer fixed a lot of important bugs for Linux Kerberos usage. But this created a new problem since SqlClient ships in out-of-band NuGet packages separate from the .NET Core runtime. SqlClient builds out of the CoreFx repo and uses the common source includes for Kerberos authentication. This created an unexpected dependency on the System.Net.Security.Native shim layer. The recent changes to these API signatures caused problems with different combinations of SqlClient NuGet packages and .NET Core 2.x versus .NET Core 3.0. After discussion with the SqlClient team, we decided to rework the changes to these native APIs so that they would remain compatible across all .NET Core versions. Long-term, the plan is to implement #36896 to expose a Kerberos API in .NET Core which could be used by SqlClient and other consumers. Closes #37183 Closes #25205
The latest changes to the System.Net.Security.Native shim layer fixed a lot of important bugs for Linux Kerberos usage. But this created a new problem since SqlClient ships in out-of-band NuGet packages separate from the .NET Core runtime. SqlClient builds out of the CoreFx repo and uses the common source includes for Kerberos authentication. This created an unexpected dependency on the System.Net.Security.Native shim layer. The recent changes to these API signatures caused problems with different combinations of SqlClient NuGet packages and .NET Core 2.x versus .NET Core 3.0. After discussion with the SqlClient team, we decided to rework the changes to these native APIs so that they would remain compatible across all .NET Core versions. Long-term, the plan is to implement #36896 to expose a Kerberos API in .NET Core which could be used by SqlClient and other consumers. Closes #37183 Closes #25205
I believe this api would also enable more WCF scenarios. We have a scenario enabled by the api SecurityBindingElement.CreateSspiNegotiationOverTransportBindingElement which makes native calls to Sspi. By exposing the NTAuth Negotiate API's, I believe WCF would be able to support this important enterprise scenario. |
This is the first of several PRs that add Enterprise Scenarios Testing capability to the repo. This PR focusses on Linux which allows for docker containers to be used in an enterprise network configuration. I focussed on 2 workflows: 1) The 'dev' workflow, and 2) The PR/CI workflow. The dev workflow works well since it's using containers in a docker-compose environment along with volume mounting your current dev's repo enlistment. The PR/CI workflow gives us an Azure DevOps pipeline to automate verification. I still need to work with the infra team to add a real pipeline that will run. I can't do that until this is merged. In the meantime, I have my own DevOps pipeline that verified this PR. See: https://dev.azure.com/systemnetncl/Enterprise%20Testing/_build/results?buildId=141 I will be linking a follow-up GitHub issue describing the roadmap for building on this system including adding Windows environments, NTLM protocol, proxies, and other libraries such as System.Net.Mail and System.Data.SqlClient. Those libraries also use Negotiate/Kerberos/NTLM enterprise-oriented protocols. Contributes to: https://github.com/dotnet/corefx/issues/41652 https://github.com/dotnet/corefx/issues/41489 https://github.com/dotnet/corefx/issues/36896 https://github.com/dotnet/corefx/issues/30150 https://github.com/dotnet/corefx/issues/24707 https://github.com/dotnet/corefx/issues/10041 https://github.com/dotnet/corefx/issues/6606 https://github.com/dotnet/corefx/issues/6161
This is the first of several PRs that add Enterprise Scenarios Testing capability to the repo. This PR focusses on Linux which allows for docker containers to be used in an enterprise network configuration. I focussed on 2 workflows: 1) The 'dev' workflow, and 2) The PR/CI workflow. The dev workflow works well since it's using containers in a docker-compose environment along with volume mounting your current dev's repo enlistment. The PR/CI workflow gives us an Azure DevOps pipeline to automate verification. I still need to work with the infra team to add a real pipeline that will run. I can't do that until this is merged. In the meantime, I have my own DevOps pipeline that verified this PR. See: https://dev.azure.com/systemnetncl/Enterprise%20Testing/_build/results?buildId=141 I will be linking a follow-up GitHub issue describing the roadmap for building on this system including adding Windows environments, NTLM protocol, proxies, and other libraries such as System.Net.Mail and System.Data.SqlClient. Those libraries also use Negotiate/Kerberos/NTLM enterprise-oriented protocols. Contributes to: https://github.com/dotnet/corefx/issues/41652 https://github.com/dotnet/corefx/issues/41489 https://github.com/dotnet/corefx/issues/36896 https://github.com/dotnet/corefx/issues/30150 https://github.com/dotnet/corefx/issues/24707 https://github.com/dotnet/corefx/issues/10041 https://github.com/dotnet/corefx/issues/6606 https://github.com/dotnet/corefx/issues/6161 * Address PR feedback * Change pipeline *.yml to only run on selected filepaths for PRs * Change kdc container Dockerfile to be based on ubuntu:18.04 * Fix typo in README.md * Update .yml file * Link (instead of copy) apache kerb module to the right place
@joperezr thank you for linking the issue. Because of lack of kerberos authentication , we are on windows servers which is more costly in terms of operation, hoping to find or implement a solution as soon as possible. |
This is mirroring the current Issues I see with the proposal:
|
lets give it try |
I gave this a closer look, so let's iterate:
There's a common part of the interface for both a client and a server once the mutual authentication is established but also a large part of the surface is different. Ideally, I would like to see two classes with a common parent. Keeping with the current naming scheme that would be Alternative proposal: Keep it as one class but provide different constructors for the server-side and client-side scenario. The downside is that
The name is not quite descriptive though - what is completed? It's the initial authentication exchange. It essentially means that now we have established a session where Java seems to call the equivalent Alternative proposal: Maybe there should be a separate class for an established session with the appropriate methods. Then we could expose
Looks reasonable.
I am not familiar with the server-side mechanics here.
The error handling is grossly underspecified here. Essentially, on the implementation side this calls into GSSAPI which returns an error code. On the managed side this is exposed as Also, only the
I am not sure where to even start with these... My initial assumption was that they map to the My take is that these methods should wrap the
On re-reading I realized that this supposed to be an API that calls into
Let's spanify those. What is the return value? Do we have any current usage that exposes the sequence numbers as datagram protocol? If not, do we want to drop it? If yes, should we add it to signature APIs and can the underlying SSPI/GSSAPI do that? |
Looking again at the SMTP code:
After rereading the code, comments and the specification I realized it's actually implementing part of the SASL GSSAPI mechanism that is defined in terms of the GssWrap/GssUnwrap operations with confidentiality flag turned off. |
Note AspNetCore ended up consuming the existing implementation via reflection. We'd love to be rid of that 😁. |
Thanks for pointing that out. I already linked that code in previous comment and on issue #62264 but I didn't quite emphasise it. I'm slowly moving forward with some experiments and unit tests but I'd welcome any feedback on the API shape discussed here. |
Note that Kestrel is not only code that depends on runtime internals via reflection. https://github.com/dotnet/SqlClient/blob/main/src/Microsoft.Data.SqlClient/netcore/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.NetSecurityNative.cs and related #62202 I'm not quite sure why we need Encrypt/Decrypt here but it can perhaps help with some other scenarios. |
Few more thoughts: The
I would prefer if this does not throw on authentication errors. I would push that to the caller. We may expose low lever GSSAPI codes but since that is not portable we should probably add status enum with errors we care about. And the runtime PAL should map to it as best as it can. We can still expose the platform value if somebody feels it would be useful. I would be supportive to do optionally base64. Since this is what goes on on wire, I would not expect it to contain any sensitive data. We may be able to use ArrayPool for intermediates. (or stackalloc if small enough) I would abandon arrays where possible. Since this does no do any IO, I would expect On the same note, I would only support For now, I think connection-based scenarios would be sufficient. I don't see need for datagrams any time soon but universe can prove me wrong. we had separate chat with @filipnavara and we agreed that we may proceed with minimal set needed for HTTP while sorting out SMTP/SASL, SqlClient(s) or WCF. This would allow us to (finally!) make some progress and cleanup Kestrel (and runtime) and unblock #62264. If we get enough feedback we can finish rest in 7.0 and if not perhaps shortly(?) after. |
CoreWCF is using an implementation based off of the asp.net core version. You can find it here: https://github.com/CoreWCF/CoreWCF/blob/main/src/CoreWCF.Primitives/src/CoreWCF/Security/NegotiateInternal/NegotiateInternalState.cs I agree with @wfurt about not throwing exceptions. Exceptions are expensive to throw and handle, and a malicious client can cause a server to throw at will by sending a specially crafted authentication packet. Failed authentication is a regular occurrence and not an exceptional event that you can take the cost of an Exception every time it happens. Ideally this functionality will be in a nuget package which targets netstandard2.0. The reason for this is that we would use it in the WCF client, which only targets netstandard2.0. When we release new features, so far we've been able to make them available to every supported version of .NET Core/.NET. There's no technical reason to require adopting of .NET 7+ to use this. |
I refactored the proposal significantly and I'll write one more post later with rationale behind the changes: public class NegotiateAuthenticationClientOptions
{
// Specifies the GSSAPI authentication package used for the authentication.
// Common values are Negotiate, NTLM or Kerberos. Default value is Negotiate.
public string Package { get; set; }
// The NetworkCredential that is used to establish the identity of the client.
// Default value is CrendentialCache.DefaultCredential.
public NetworkCredential Credential { get; set; }
// The Service Principal Name (SPN) that uniquely identifies the server to authenticate.
public string? TargetName { get; set; }
// The ChannelBinding that is used for extended protection.
public ChannelBinding? Binding { get; set; }
// Indicates the requires level of protection of the authentication exchange
// and any further data exchange.
// Valid values: None, Sign, EncryptAndSign
// Default value: None
System.Net.Security.ProtectionLevel RequiredProtectionLevel { get; set; }
}
public class NegotiateAuthenticationServerOptions
{
// Specifies the GSSAPI authentication package used for the authentication.
// Common values are Negotiate, NTLM or Kerberos. Default value is Negotiate.
public string Package { get; set; }
// The NetworkCredential that is used to establish the identity of the client.
// Default value is CrendentialCache.DefaultCredential.
// Note: I'm not quite sure of the meaning of this on the server side but
// it exists on GSSAPI side.
public NetworkCredential Credential { get; set; }
// The ChannelBinding that is used for extended protection.
public ChannelBinding? Binding { get; set; }
// Indicates the requires level of protection of the authentication exchange
// and any further data exchange.
// Valid values: None, Sign, EncryptAndSign
// Default value: None
System.Net.Security.ProtectionLevel RequiredProtectionLevel { get; set; }
}
public class NegotiateAuthentication : IDisposable
{
// Create client-side authentication
public NegotiateAuthentication(NegotiateAuthenticationClientOptions clientOptions);
// Create server-side authentication
public NegotiateAuthentication(NegotiateAuthenticationServerOptions serverOptions);
// Indicates whether the initial authentication finished.
// NOTE: Original it was named IsCompleted in the proposal but I renamed
// it to match the property on NegotiateStream.
public bool IsAuthenticated { get; set; }
// Indicates negotiated protection level (can be higher than the required one).
// Returns None if authentication was not finished yet.
public System.Net.Security.ProtectionLevel ProtectionLevel { get; set; }
// Indicates whether signing was negotiated.
// Returns false if authentication was not finished yet.
public bool IsSigned { get; set; }
// Indicates whether signing was negotiated.
// Returns false if authentication was not finished yet.
public bool IsEncrypted { get; set; }
// Indicates whether the client and server are mutually authenticated.
// Returns false if authentication was not finished yet.
public bool IsMutuallyAuthenticated { get; set; }
// Indicates whether this is server-side authentication context.
// Returns value based on the constructor used, provided for parity with
// NegoatiateStream.
public bool IsServer { get; set; }
// The GSSAPI package name that was used for the authentiation. Initially it's
// the name specified in the options in constructor. After successful authentication
// it should return the actual mechanism used. For example, if Negotiate is
// specified as the requested GSSAPI package this may return NTLM or Kerberos once
// authentication exchange is complete (eg. IsAuthenticated == true).
//
// NOTE: This is partly duplicate with RemoteIdentity so it may not be necessary
// on the public API.
public string Package { get; }
// For server context it returns the SPN target name of the client after successful
// authentication. For client context it returns the target name specified in the
// constructor options.
public string? TargetName { get; }
// Gets information about the identity of the remote party.
//
// When accessed by the client, this property returns a GenericIdentity containing
// the Service Principal Name (SPN) of the server and the authentication protocol used.
//
// Throws InvalidOperationException if authentication was not finished yet
// (IsAuthenticated == false) for server-side.
public System.Security.Principal.IIdentity RemoteIdentity { get };
public byte[] GetOutgoingBlob(ReadOnlySpan<byte> incomingBlob, out NegotiateAuthenticationStatusCode statusCode);
// Base64 version of GetOutgoingBlob
public string GetOutgoingBlob(string incomingBlob, out NegotiateAuthenticationStatusCode statusCode);
// TODO (APIs not necessary for HTTP authentication but necessary for high-level protocols
// like SASL and NegotiateStream):
// Wrap, Unwrap as replacement for Encrypt, Decrypt, MakeSignature and VerifySignature
// GetMIC and VerifyMIC if we find a use for that
}
// NOTE: Mirrors SecurityStatusPalErrorCode at the moment but it should mostly map to GSSAPI error
// codes.
public enum NegotiateAuthenticationStatusCode
{
NotSet = 0,
OK,
ContinueNeeded,
CompleteNeeded,
CompleteAndContinue,
ContextExpired,
CredentialsNeeded,
Renegotiate,
TryAgain,
// Errors
OutOfMemory,
InvalidHandle,
Unsupported,
TargetUnknown,
InternalError,
PackageNotFound,
NotOwner,
CannotInstall,
InvalidToken,
CannotPack,
QopNotSupported,
NoImpersonation,
LogonDenied,
UnknownCredentials,
NoCredentials,
MessageAltered,
OutOfSequence,
NoAuthenticatingAuthority,
IncompleteMessage,
IncompleteCredentials,
BufferNotEnough,
WrongPrincipal,
TimeSkew,
UntrustedRoot,
IllegalMessage,
CertUnknown,
CertExpired,
DecryptFailure,
AlgorithmMismatch,
SecurityQosFailed,
SmartcardLogonRequired,
UnsupportedPreauth,
BadBinding,
DowngradeDetected,
ApplicationProtocolMismatch,
NoRenegotiation
} |
The
I renamed the property to
You are right,
Yep for
Agreed that the API should not throw. I feel that the GSSAPI status codes would be ideal here. SSPI and GSSAPI basically produce nearly identical set of errors. For now I followed the
No particular opinion on this. I added the base64
I'm not sure whether the caller can reasonably predict the buffer sizes for
For encrypt/decrypt/sign/verify it would likely be possible to expose API to return the correct size beforehand and thus work only with
👍 I agree. The design above could easily be extended by adding option into |
The original proposal used Aside from For
I put this into |
What's the status of this for .NET 7? ASP.NET Core is being annotated for trimming and the current reflection approach isn't trim safe. A public API is needed.
There is no way to know whether the internal API is trimmed or not so it can't be safely suppressed. ASP.NET Core negotiate auth will need to be marked as unsafe until there is a new API. |
@JamesNK Discussing it with the stakeholders now to see if we can move forward with API review. |
I extracted latest revision to #69920 @filipnavara. edit it as you see fit. |
We can. My plan was to close it when #70720 is merged. (perhaps mention it this there as well so it linked and resolved) |
I added the mention so it auto-closes. |
Please expose a high level API for managing Negotiate/NTLM/Kerberos authentication handshakes. This is not the same as https://github.com/dotnet/corefx/issues/32291, which wants a low level Kerberos specific API.
There is an internal API today called NTAuthentication that is used by HttpClient, HttpListener, and NegotiateStream to exchange opaque auth blobs, negotiate the authentication protocol, and identify the client to the server. ASP.NET Core also wants to expose this functionality for our cross platform server Kestrel. It could use the same NTAuthentication APIs and handle the HTTP aspect itself.
This is related to https://github.com/dotnet/corefx/issues/8221 for cross platform server support.
A general purpose API that could be used by NegotiateStream, SocketsHttpHandler, and HttpListener would be like this:
Note this does expose ContextFlagsPal. I only see the constructor parameter used once or twice and it could likely be abstracted to a bool or similar.
The text was updated successfully, but these errors were encountered: