-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Closed
Labels
api-approvedAPI was approved in API review, it can be implementedAPI was approved in API review, it can be implementedarea-System.Net.SecurityblockingMarks issues that we want to fast track in order to unblock other important workMarks issues that we want to fast track in order to unblock other important work
Milestone
Description
Background and motivation
runtime already have very similar implementation - used primarily by SocketsHttphandler and partially by SMTP.
Kestrel currently use this internal API via Reflection (#29270). Additionally, we would like to have similar authentication in Android handler that lives outside of runtime repo.
Tha main goal is to expose existing functionality via public API so other components can leverage it as well.
API Proposal
namespace System.Net.Security
{
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
}
}API Usage
var auth = new NegotiateAuthentication(new NegotiateAuthenticationClientOptions("NTLM", testCredential "HTTP/foo"));
NegotiateAuthenticationStatusCode statusCode;
byte[]? negotiateBlob = ntAuth.GetOutgoingBlob(ReadOnlySpan<byte>.Empty, out NegotiateAuthenticationStatusCode statusCode);
sendBlob(negotiateBlob);
do
{
buffer = receiveBlob();
byte[]? negotiateBlob = ntAuth.GetOutgoingBlob(buffer, out statusCode);
} while (statusCode == NegotiateAuthenticationStatusCode .ContinueNeeded);
if (statusCode == NegotiateAuthenticationStatusCode.OK)
{
....
}
else
{
...
}Alternative Designs
No response
Risks
This API currently works well for bot client and server side of HTTP. We really have weak test coverage for SMTP and other protocols.
Metadata
Metadata
Assignees
Labels
api-approvedAPI was approved in API review, it can be implementedAPI was approved in API review, it can be implementedarea-System.Net.SecurityblockingMarks issues that we want to fast track in order to unblock other important workMarks issues that we want to fast track in order to unblock other important work