diff --git a/src/Common/src/Interop/Unix/System.Net.Security.Native/SecuritySafeHandles.cs b/src/Common/src/Interop/Unix/System.Net.Security.Native/SecuritySafeHandles.cs
new file mode 100644
index 000000000000..eaedd736d026
--- /dev/null
+++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/SecuritySafeHandles.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+namespace System.Net.Security
+{
+ internal sealed class SafeFreeNegoCredentials : SafeFreeCredentials
+ {
+ private SafeGssCredHandle _credential;
+
+ public SafeGssCredHandle GssCredential
+ {
+ get { return _credential; }
+ }
+
+ public SafeFreeNegoCredentials(string username, string password, string domain) : base(IntPtr.Zero, true)
+ {
+ _credential = SafeGssCredHandle.Create(username, password, domain);
+ }
+
+ public override bool IsInvalid
+ {
+ get { return (null == _credential); }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ _credential.Dispose();
+ _credential = null;
+ return true;
+ }
+ }
+
+ internal sealed class SafeDeleteNegoContext : SafeDeleteContext
+ {
+ private SafeGssNameHandle _targetName;
+ private SafeGssContextHandle _context;
+
+ public SafeGssNameHandle TargetName
+ {
+ get { return _targetName; }
+ }
+
+ public SafeGssContextHandle GssContext
+ {
+ get { return _context; }
+ }
+
+ public SafeDeleteNegoContext(SafeFreeNegoCredentials credential, string targetName)
+ : base(credential)
+ {
+ try
+ {
+ _targetName = SafeGssNameHandle.Create(targetName, false);
+ }
+ catch
+ {
+ base.ReleaseHandle();
+ throw;
+ }
+ }
+
+ public void SetGssContext(SafeGssContextHandle context)
+ {
+ Debug.Assert(!context.IsInvalid, "Invalid context passed to SafeDeleteNegoContext");
+ _context = context;
+ }
+
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ if (null != _context)
+ {
+ _context.Dispose();
+ _context = null;
+ }
+ _targetName.Dispose();
+ _targetName = null;
+ }
+ base.Dispose(disposing);
+ }
+ }
+}
diff --git a/src/Common/src/Interop/Unix/libssl/SecuritySafeHandles.cs b/src/Common/src/Interop/Unix/libssl/SecuritySafeHandles.cs
index 2f4ae05d3699..0b4427a5f3c0 100644
--- a/src/Common/src/Interop/Unix/libssl/SecuritySafeHandles.cs
+++ b/src/Common/src/Interop/Unix/libssl/SecuritySafeHandles.cs
@@ -55,12 +55,19 @@ protected override bool ReleaseHandle()
// Implementation of handles dependable on FreeCredentialsHandle
//
#if DEBUG
- internal sealed class SafeFreeCredentials : DebugSafeHandle
+ internal abstract class SafeFreeCredentials : DebugSafeHandle
{
#else
- internal sealed class SafeFreeCredentials : SafeHandle
+ internal abstract class SafeFreeCredentials : SafeHandle
{
#endif
+ protected SafeFreeCredentials(IntPtr handle, bool ownsHandle) : base(handle, ownsHandle)
+ {
+ }
+ }
+
+ internal sealed class SafeFreeSslCredentials : SafeFreeCredentials
+ {
private SafeX509Handle _certHandle;
private SafeEvpPKeyHandle _certKeyHandle;
private SslProtocols _protocols = SslProtocols.None;
@@ -86,7 +93,7 @@ internal EncryptionPolicy Policy
get { return _policy; }
}
- public SafeFreeCredentials(X509Certificate certificate, SslProtocols protocols, EncryptionPolicy policy)
+ public SafeFreeSslCredentials(X509Certificate certificate, SslProtocols protocols, EncryptionPolicy policy)
: base(IntPtr.Zero, true)
{
Debug.Assert(
@@ -206,13 +213,49 @@ protected override bool ReleaseHandle()
}
#if DEBUG
- internal sealed class SafeDeleteContext : DebugSafeHandle
+ internal abstract class SafeDeleteContext : DebugSafeHandle
{
#else
- internal sealed class SafeDeleteContext : SafeHandle
+ internal abstract class SafeDeleteContext : SafeHandle
{
#endif
- private readonly SafeFreeCredentials _credential;
+ private SafeFreeCredentials _credential;
+
+ protected SafeDeleteContext(SafeFreeCredentials credential)
+ : base(IntPtr.Zero, true)
+ {
+ Debug.Assert((null != credential), "Invalid credential passed to SafeDeleteContext");
+
+ // When a credential handle is first associated with the context we keep credential
+ // ref count bumped up to ensure ordered finalization. The credential properties
+ // are used in the SSL/NEGO data structures and should survive the lifetime of
+ // the SSL/NEGO context
+ bool ignore = false;
+ _credential = credential;
+ _credential.DangerousAddRef(ref ignore);
+ }
+
+ public override bool IsInvalid
+ {
+ get { return (null == _credential); }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Debug.Assert((null != _credential), "Null credential in SafeDeleteContext");
+ _credential.DangerousRelease();
+ _credential = null;
+ return true;
+ }
+
+ public override string ToString()
+ {
+ return IsInvalid ? String.Empty : handle.ToString();
+ }
+ }
+
+ internal sealed class SafeDeleteSslContext : SafeDeleteContext
+ {
private readonly SafeSslHandle _sslContext;
public SafeSslHandle SslContext
@@ -223,18 +266,10 @@ public SafeSslHandle SslContext
}
}
- public SafeDeleteContext(SafeFreeCredentials credential, bool isServer, bool remoteCertRequired)
- : base(IntPtr.Zero, true)
+ public SafeDeleteSslContext(SafeFreeSslCredentials credential, bool isServer, bool remoteCertRequired)
+ : base(credential)
{
- Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteContext");
-
- // When a credential handle is first associated with the context we keep credential
- // ref count bumped up to ensure ordered finalization. The certificate handle and
- // key handle are used in the SSL data structures and should survive the lifetime of
- // the SSL context
- bool gotCredRef = false;
- _credential = credential;
- _credential.DangerousAddRef(ref gotCredRef);
+ Debug.Assert((null != credential) && !credential.IsInvalid, "Invalid credential used in SafeDeleteSslContext");
try
{
@@ -248,11 +283,8 @@ public SafeDeleteContext(SafeFreeCredentials credential, bool isServer, bool rem
}
catch(Exception ex)
{
- if (gotCredRef)
- {
- _credential.DangerousRelease();
- }
Debug.Write("Exception Caught. - " + ex);
+ base.ReleaseHandle();
throw;
}
}
@@ -265,13 +297,6 @@ public override bool IsInvalid
}
}
- protected override bool ReleaseHandle()
- {
- Debug.Assert((null != _credential) && !_credential.IsInvalid, "Invalid credential saved in SafeDeleteContext");
- _credential.DangerousRelease();
- return true;
- }
-
protected override void Dispose(bool disposing)
{
if (disposing)
diff --git a/src/Common/src/Interop/Windows/sspicli/NegotiationInfoClass.cs b/src/Common/src/Interop/Windows/sspicli/NegotiationInfoClass.cs
index 3a3149598b7a..1fcded4950d9 100644
--- a/src/Common/src/Interop/Windows/sspicli/NegotiationInfoClass.cs
+++ b/src/Common/src/Interop/Windows/sspicli/NegotiationInfoClass.cs
@@ -7,12 +7,8 @@ namespace System.Net
{
// This class is used to determine if NTLM or
// Kerberos are used in the context of a Negotiate handshake
- internal class NegotiationInfoClass
+ internal partial class NegotiationInfoClass
{
- internal const string NTLM = "NTLM";
- internal const string Kerberos = "Kerberos";
- internal const string WDigest = "WDigest";
- internal const string Negotiate = "Negotiate";
internal string AuthenticationPackage;
internal NegotiationInfoClass(SafeHandle safeHandle, int negotiationState)
diff --git a/src/System.Net.Security/src/System.Net.Security.csproj b/src/System.Net.Security/src/System.Net.Security.csproj
index f525e1ff2dd3..1345462a56b1 100644
--- a/src/System.Net.Security/src/System.Net.Security.csproj
+++ b/src/System.Net.Security/src/System.Net.Security.csproj
@@ -48,9 +48,15 @@
+
+
+
+
+
+
@@ -143,10 +149,7 @@
-
-
-
@@ -292,6 +295,21 @@
Common\Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs
+
+ Common\Interop\Unix\System.Net.Security.Native\Interop.GssApi.cs
+
+
+ Common\Interop\Unix\System.Net.Security.Native\Interop.GssApiException.cs
+
+
+ Common\Interop\Unix\System.Net.Security.Native\SecuritySafeHandles.cs
+
+
+ Common\Microsoft\Win32\SafeHandles\GssSafeHandles.cs
+
+
+ Common\Interop\Unix\System.Net.Security.Native\Interop.NetSecurity.cs
+
Common\Microsoft\Win32\SafeHandles\SafeX509Handles.Unix.cs
diff --git a/src/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs b/src/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs
index 213567bd1887..290819784999 100644
--- a/src/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs
+++ b/src/System.Net.Security/src/System/Net/CertificateValidationPal.Unix.cs
@@ -112,7 +112,7 @@ internal static X509Certificate2 GetRemoteCertificate(SafeDeleteContext security
remoteCertificateStore = new X509Certificate2Collection();
using (SafeSharedX509StackHandle chainStack =
- Interop.OpenSsl.GetPeerCertificateChain(securityContext.SslContext))
+ Interop.OpenSsl.GetPeerCertificateChain(((SafeDeleteSslContext)securityContext).SslContext))
{
if (!chainStack.IsInvalid)
{
@@ -162,7 +162,7 @@ internal static X509Certificate2 GetRemoteCertificate(SafeDeleteContext security
//
internal static string[] GetRequestCertificateAuthorities(SafeDeleteContext securityContext)
{
- using (SafeSharedX509NameStackHandle names = Interop.Ssl.SslGetClientCAList(securityContext.SslContext))
+ using (SafeSharedX509NameStackHandle names = Interop.Ssl.SslGetClientCAList(((SafeDeleteSslContext)securityContext).SslContext))
{
if (names.IsInvalid)
{
@@ -256,7 +256,7 @@ private static int QueryContextRemoteCertificate(SafeDeleteContext securityConte
remoteCertContext = null;
try
{
- SafeX509Handle remoteCertificate = Interop.OpenSsl.GetPeerCertificate(securityContext.SslContext);
+ SafeX509Handle remoteCertificate = Interop.OpenSsl.GetPeerCertificate(((SafeDeleteSslContext)securityContext).SslContext);
// Note that cert ownership is transferred to SafeFreeCertContext
remoteCertContext = new SafeFreeCertContext(remoteCertificate);
return 0;
diff --git a/src/System.Net.Security/src/System/Net/ContextFlagsPal.cs b/src/System.Net.Security/src/System/Net/ContextFlagsPal.cs
new file mode 100644
index 000000000000..8da411e816cd
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/ContextFlagsPal.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+
+namespace System.Net
+{
+ [Flags]
+ internal enum ContextFlagsPal
+ {
+ Zero = 0,
+ Delegate = 0x00000001,
+ MutualAuth = 0x00000002,
+ ReplayDetect = 0x00000004,
+ SequenceDetect = 0x00000008,
+ Confidentiality = 0x00000010,
+ UseSessionKey = 0x00000020,
+ AllocateMemory = 0x00000100,
+ Connection = 0x00000800,
+ InitExtendedError = 0x00004000,
+ AcceptExtendedError = 0x00008000,
+ InitStream = 0x00008000,
+ AcceptStream = 0x00010000,
+ InitIntegrity = 0x00010000,
+ AcceptIntegrity = 0x00020000,
+ InitManualCredValidation = 0x00080000,
+ InitUseSuppliedCreds = 0x00000080,
+ InitIdentify = 0x00020000,
+ AcceptIdentify = 0x00080000,
+ ProxyBindings = 0x04000000,
+ AllowMissingBindings = 0x10000000,
+ UnverifiedTargetName = 0x20000000,
+ }
+}
diff --git a/src/System.Net.Security/src/System/Net/NTAuthentication.cs b/src/System.Net.Security/src/System/Net/NTAuthentication.cs
index bfe544f1d140..5deb98af009b 100644
--- a/src/System.Net.Security/src/System/Net/NTAuthentication.cs
+++ b/src/System.Net.Security/src/System/Net/NTAuthentication.cs
@@ -23,12 +23,11 @@ internal class NTAuthentication
private string _clientSpecifiedSpn;
private int _tokenSize;
- private Interop.SspiCli.ContextFlags _requestedContextFlags;
- private Interop.SspiCli.ContextFlags _contextFlags;
+ private ContextFlagsPal _requestedContextFlags;
+ private ContextFlagsPal _contextFlags;
private bool _isCompleted;
private string _protocolName;
- private SecSizes _sizes;
private string _lastProtocolName;
private string _package;
@@ -57,10 +56,10 @@ internal string AssociatedName
{
if (!(IsValidContext && IsCompleted))
{
- throw new Win32Exception((int)Interop.SecurityStatus.InvalidHandle);
+ throw new Exception(SR.net_auth_noauth);
}
- string name = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, _securityContext, Interop.SspiCli.ContextAttribute.Names) as string;
+ string name = NegoState.QueryContextAssociatedName(_securityContext);
if (GlobalLog.IsEnabled)
{
GlobalLog.Print("NTAuthentication: The context is associated with [" + name + "]");
@@ -73,7 +72,7 @@ internal bool IsConfidentialityFlag
{
get
{
- return (_contextFlags & Interop.SspiCli.ContextFlags.Confidentiality) != 0;
+ return (_contextFlags & ContextFlagsPal.Confidentiality) != 0;
}
}
@@ -81,7 +80,7 @@ internal bool IsIntegrityFlag
{
get
{
- return (_contextFlags & (_isServer ? Interop.SspiCli.ContextFlags.AcceptIntegrity : Interop.SspiCli.ContextFlags.InitIntegrity)) != 0;
+ return (_contextFlags & (_isServer ? ContextFlagsPal.AcceptIntegrity : ContextFlagsPal.InitIntegrity)) != 0;
}
}
@@ -89,7 +88,7 @@ internal bool IsMutualAuthFlag
{
get
{
- return (_contextFlags & Interop.SspiCli.ContextFlags.MutualAuth) != 0;
+ return (_contextFlags & ContextFlagsPal.MutualAuth) != 0;
}
}
@@ -97,7 +96,7 @@ internal bool IsDelegationFlag
{
get
{
- return (_contextFlags & Interop.SspiCli.ContextFlags.Delegate) != 0;
+ return (_contextFlags & ContextFlagsPal.Delegate) != 0;
}
}
@@ -105,7 +104,7 @@ internal bool IsIdentifyFlag
{
get
{
- return (_contextFlags & (_isServer ? Interop.SspiCli.ContextFlags.AcceptIdentify : Interop.SspiCli.ContextFlags.InitIdentify)) != 0;
+ return (_contextFlags & (_isServer ? ContextFlagsPal.AcceptIdentify : ContextFlagsPal.InitIdentify)) != 0;
}
}
@@ -174,65 +173,34 @@ internal string ProtocolName
// Note: May return string.Empty if the auth is not done yet or failed.
if (_protocolName == null)
{
- NegotiationInfoClass negotiationInfo = null;
+ string negotiationAuthenticationPackage = null;
if (IsValidContext)
{
- negotiationInfo = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, _securityContext, Interop.SspiCli.ContextAttribute.NegotiationInfo) as NegotiationInfoClass;
+ negotiationAuthenticationPackage = NegoState.QueryContextAuthenticationPackage(_securityContext);
if (IsCompleted)
{
- if (negotiationInfo != null)
- {
- // Cache it only when it's completed.
- _protocolName = negotiationInfo.AuthenticationPackage;
- }
+ _protocolName = negotiationAuthenticationPackage;
}
}
-
- return negotiationInfo == null ? string.Empty : negotiationInfo.AuthenticationPackage;
+ return negotiationAuthenticationPackage ?? string.Empty;
}
return _protocolName;
}
}
- internal SecSizes Sizes
- {
- get
- {
- if ((IsCompleted && IsValidContext))
- {
- if (GlobalLog.IsEnabled)
- {
- GlobalLog.Assert("NTAuthentication#{0}::MaxDataSize|The context is not completed or invalid.", LoggingHash.HashString(this));
- }
- Debug.Fail("NTAuthentication#" + LoggingHash.HashString(this) + "::MaxDataSize |The context is not completed or invalid.");
- }
-
- if (_sizes == null)
- {
- _sizes = SSPIWrapper.QueryContextAttributes(
- GlobalSSPI.SSPIAuth,
- _securityContext,
- Interop.SspiCli.ContextAttribute.Sizes
- ) as SecSizes;
- }
-
- return _sizes;
- }
- }
-
//
// This overload does not attempt to impersonate because the caller either did it already or the original thread context is still preserved.
//
- internal NTAuthentication(bool isServer, string package, NetworkCredential credential, string spn, Interop.SspiCli.ContextFlags requestedContextFlags, ChannelBinding channelBinding)
+ internal NTAuthentication(bool isServer, string package, NetworkCredential credential, string spn, ContextFlagsPal requestedContextFlags, ChannelBinding channelBinding)
{
Initialize(isServer, package, credential, spn, requestedContextFlags, channelBinding);
}
private class InitializeCallbackContext
{
- internal InitializeCallbackContext(NTAuthentication thisPtr, bool isServer, string package, NetworkCredential credential, string spn, Interop.SspiCli.ContextFlags requestedContextFlags, ChannelBinding channelBinding)
+ internal InitializeCallbackContext(NTAuthentication thisPtr, bool isServer, string package, NetworkCredential credential, string spn, ContextFlagsPal requestedContextFlags, ChannelBinding channelBinding)
{
ThisPtr = thisPtr;
IsServer = isServer;
@@ -248,7 +216,7 @@ internal InitializeCallbackContext(NTAuthentication thisPtr, bool isServer, stri
internal readonly string Package;
internal readonly NetworkCredential Credential;
internal readonly string Spn;
- internal readonly Interop.SspiCli.ContextFlags RequestedContextFlags;
+ internal readonly ContextFlagsPal RequestedContextFlags;
internal readonly ChannelBinding ChannelBinding;
}
@@ -258,14 +226,14 @@ private static void InitializeCallback(object state)
context.ThisPtr.Initialize(context.IsServer, context.Package, context.Credential, context.Spn, context.RequestedContextFlags, context.ChannelBinding);
}
- private void Initialize(bool isServer, string package, NetworkCredential credential, string spn, Interop.SspiCli.ContextFlags requestedContextFlags, ChannelBinding channelBinding)
+ private void Initialize(bool isServer, string package, NetworkCredential credential, string spn, ContextFlagsPal requestedContextFlags, ChannelBinding channelBinding)
{
if (GlobalLog.IsEnabled)
{
GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::.ctor() package:" + LoggingHash.ObjectToString(package) + " spn:" + LoggingHash.ObjectToString(spn) + " flags :" + requestedContextFlags.ToString());
}
- _tokenSize = SSPIWrapper.GetVerifyPackageInfo(GlobalSSPI.SSPIAuth, package, true).MaxToken;
+ _tokenSize = NegoState.QueryMaxTokenSize(package);
_isServer = isServer;
_spn = spn;
_securityContext = null;
@@ -290,55 +258,17 @@ private void Initialize(bool isServer, string package, NetworkCredential credent
GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::.ctor(): using DefaultCredentials");
}
- _credentialsHandle = SSPIWrapper.AcquireDefaultCredential(
- GlobalSSPI.SSPIAuth,
- package,
- (_isServer ? Interop.SspiCli.CredentialUse.Inbound : Interop.SspiCli.CredentialUse.Outbound));
+ _credentialsHandle = NegoState.AcquireDefaultCredential(package, _isServer);
}
else
{
- unsafe
- {
- SafeSspiAuthDataHandle authData = null;
- try
- {
- Interop.SecurityStatus result = Interop.SspiCli.SspiEncodeStringsAsAuthIdentity(
- credential.UserName, credential.Domain,
- credential.Password, out authData);
-
- if (result != Interop.SecurityStatus.OK)
- {
- if (NetEventSource.Log.IsEnabled())
- {
- NetEventSource.PrintError(
- NetEventSource.ComponentType.Security,
- SR.Format(
- SR.net_log_operation_failed_with_error,
- "SspiEncodeStringsAsAuthIdentity()",
- String.Format(CultureInfo.CurrentCulture, "0x{0:X}", (int)result)));
- }
-
- throw new Win32Exception((int)result);
- }
-
- _credentialsHandle = SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPIAuth,
- package, (_isServer ? Interop.SspiCli.CredentialUse.Inbound : Interop.SspiCli.CredentialUse.Outbound), ref authData);
- }
- finally
- {
- if (authData != null)
- {
- authData.Dispose();
- }
- }
- }
+ _credentialsHandle = NegoState.AcquireCredentialsHandle(package, _isServer, credential);
}
}
- // This will return a client token when conducted authentication on server side.
- // This token can be used for impersonation. We use it to create a WindowsIdentity and hand it out to the server app.
- internal SecurityContextTokenHandle GetContextToken(out Interop.SecurityStatus status)
+ internal SafeDeleteContext GetContext(out SecurityStatusPal status)
{
+ status = SecurityStatusPal.OK;
if ((IsCompleted && IsValidContext))
{
if (GlobalLog.IsEnabled)
@@ -359,28 +289,11 @@ internal SecurityContextTokenHandle GetContextToken(out Interop.SecurityStatus s
if (!IsValidContext)
{
- throw new Win32Exception((int)Interop.SecurityStatus.InvalidHandle);
- }
-
- SecurityContextTokenHandle token = null;
- status = (Interop.SecurityStatus)SSPIWrapper.QuerySecurityContextToken(
- GlobalSSPI.SSPIAuth,
- _securityContext,
- out token);
-
- return token;
- }
-
- internal SecurityContextTokenHandle GetContextToken()
- {
- Interop.SecurityStatus status;
- SecurityContextTokenHandle token = GetContextToken(out status);
- if (status != Interop.SecurityStatus.OK)
- {
- throw new Win32Exception((int)status);
+ status = SecurityStatusPal.InvalidHandle;
+ return null;
}
- return token;
+ return _securityContext;
}
internal void CloseContext()
@@ -392,7 +305,7 @@ internal void CloseContext()
}
// Accepts an incoming binary security blob and returns an outgoing binary security blob.
- internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out Interop.SecurityStatus statusCode)
+ internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out SecurityStatusPal statusCode)
{
if (GlobalLog.IsEnabled)
{
@@ -425,13 +338,11 @@ internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out Inte
if (!_isServer)
{
// client session
- statusCode = (Interop.SecurityStatus)SSPIWrapper.InitializeSecurityContext(
- GlobalSSPI.SSPIAuth,
+ statusCode = NegoState.InitializeSecurityContext(
_credentialsHandle,
ref _securityContext,
_spn,
_requestedContextFlags,
- Interop.SspiCli.Endianness.Network,
inSecurityBufferArray,
outSecurityBuffer,
ref _contextFlags);
@@ -441,15 +352,12 @@ internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out Inte
GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingBlob() SSPIWrapper.InitializeSecurityContext() returns statusCode:0x" + ((int)statusCode).ToString("x8", NumberFormatInfo.InvariantInfo) + " (" + statusCode.ToString() + ")");
}
- if (statusCode == Interop.SecurityStatus.CompleteNeeded)
+ if (statusCode == SecurityStatusPal.CompleteNeeded)
{
var inSecurityBuffers = new SecurityBuffer[1];
inSecurityBuffers[0] = outSecurityBuffer;
- statusCode = (Interop.SecurityStatus)SSPIWrapper.CompleteAuthToken(
- GlobalSSPI.SSPIAuth,
- ref _securityContext,
- inSecurityBuffers);
+ statusCode = NegoState.CompleteAuthToken(ref _securityContext, inSecurityBuffers);
if (GlobalLog.IsEnabled)
{
@@ -462,12 +370,10 @@ internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out Inte
else
{
// Server session.
- statusCode = (Interop.SecurityStatus)SSPIWrapper.AcceptSecurityContext(
- GlobalSSPI.SSPIAuth,
+ statusCode = NegoState.AcceptSecurityContext(
_credentialsHandle,
ref _securityContext,
_requestedContextFlags,
- Interop.SspiCli.Endianness.Network,
inSecurityBufferArray,
outSecurityBuffer,
ref _contextFlags);
@@ -493,13 +399,13 @@ internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out Inte
}
- if (((int)statusCode & unchecked((int)0x80000000)) != 0)
+ if (NegoState.IsError(statusCode))
{
CloseContext();
_isCompleted = true;
if (throwOnError)
{
- var exception = new Win32Exception((int)statusCode);
+ Exception exception = NegoState.CreateExceptionFromError(statusCode);
if (GlobalLog.IsEnabled)
{
GlobalLog.Leave("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingBlob", "Win32Exception:" + exception);
@@ -519,21 +425,18 @@ internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out Inte
SSPIHandleCache.CacheCredential(_credentialsHandle);
}
- // The return value from SSPI will tell us correctly if the
- // handshake is over or not: http://msdn.microsoft.com/library/psdk/secspi/sspiref_67p0.htm
- // we also have to consider the case in which SSPI formed a new context, in this case we're done as well.
- if (statusCode == Interop.SecurityStatus.OK)
+ // The return value will tell us correctly if the handshake is over or not
+ if (statusCode == SecurityStatusPal.OK)
{
// Success.
- if ((statusCode == Interop.SecurityStatus.OK))
+ if (GlobalLog.IsEnabled)
{
- if (GlobalLog.IsEnabled)
- {
- GlobalLog.AssertFormat("NTAuthentication#{0}::GetOutgoingBlob()|statusCode:[0x{1:x8}] ({2}) m_SecurityContext#{3}::Handle:[{4}] [STATUS != OK]", LoggingHash.HashString(this), (int)statusCode, statusCode, LoggingHash.HashString(_securityContext), LoggingHash.ObjectToString(_securityContext));
- }
- Debug.Fail("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingBlob()|statusCode:[0x" + ((int)statusCode).ToString("x8") + "] (" + statusCode + ") m_SecurityContext#" + LoggingHash.HashString(_securityContext) + "::Handle:[" + LoggingHash.ObjectToString(_securityContext) + "] [STATUS != OK]");
+ GlobalLog.AssertFormat(
+ "NTAuthentication#{0}::GetOutgoingBlob()|statusCode:[0x{1:x8}] ({2}) m_SecurityContext#{3}::Handle:[{4}] [STATUS != OK]",
+ LoggingHash.HashString(this), (int) statusCode, statusCode,
+ LoggingHash.HashString(_securityContext), LoggingHash.ObjectToString(_securityContext));
}
-
+ Debug.Fail("NTAuthentication#" + LoggingHash.HashString(this) + "::GetOutgoingBlob()|statusCode:[0x" + ((int)statusCode).ToString("x8") + "] (" + statusCode + ") m_SecurityContext#" + LoggingHash.HashString(_securityContext) + "::Handle:[" + LoggingHash.ObjectToString(_securityContext) + "] [STATUS != OK]");
_isCompleted = true;
}
else if (GlobalLog.IsEnabled)
@@ -552,86 +455,15 @@ internal byte[] GetOutgoingBlob(byte[] incomingBlob, bool throwOnError, out Inte
internal int Encrypt(byte[] buffer, int offset, int count, ref byte[] output, uint sequenceNumber)
{
- SecSizes sizes = Sizes;
-
- try
- {
- int maxCount = checked(Int32.MaxValue - 4 - sizes.BlockSize - sizes.SecurityTrailer);
-
- if (count > maxCount || count < 0)
- {
- throw new ArgumentOutOfRangeException("count", SR.Format(SR.net_io_out_range, maxCount));
- }
- }
- catch (Exception e)
- {
- if (!ExceptionCheck.IsFatal(e))
- {
- if (GlobalLog.IsEnabled)
- {
- GlobalLog.Assert("NTAuthentication#" + LoggingHash.HashString(this) + "::Encrypt", "Arguments out of range.");
- }
- Debug.Fail("NTAuthentication#" + LoggingHash.HashString(this) + "::Encrypt", "Arguments out of range.");
- }
-
- throw;
- }
-
- int resultSize = count + sizes.SecurityTrailer + sizes.BlockSize;
- if (output == null || output.Length < resultSize + 4)
- {
- output = new byte[resultSize + 4];
- }
-
- // Make a copy of user data for in-place encryption.
- Buffer.BlockCopy(buffer, offset, output, 4 + sizes.SecurityTrailer, count);
-
- // Prepare buffers TOKEN(signature), DATA and Padding.
- var securityBuffer = new SecurityBuffer[3];
- securityBuffer[0] = new SecurityBuffer(output, 4, sizes.SecurityTrailer, SecurityBufferType.Token);
- securityBuffer[1] = new SecurityBuffer(output, 4 + sizes.SecurityTrailer, count, SecurityBufferType.Data);
- securityBuffer[2] = new SecurityBuffer(output, 4 + sizes.SecurityTrailer + count, sizes.BlockSize, SecurityBufferType.Padding);
-
- int errorCode;
- if (IsConfidentialityFlag)
- {
- errorCode = SSPIWrapper.EncryptMessage(GlobalSSPI.SSPIAuth, _securityContext, securityBuffer, sequenceNumber);
- }
- else
- {
- if (IsNTLM)
- {
- securityBuffer[1].type |= SecurityBufferType.ReadOnlyFlag;
- }
-
- errorCode = SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, _securityContext, securityBuffer, 0);
- }
-
- if (errorCode != 0)
- {
- if (GlobalLog.IsEnabled)
- {
- GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::Encrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
- }
- throw new Win32Exception(errorCode);
- }
-
- // Compacting the result.
- resultSize = securityBuffer[0].size;
- bool forceCopy = false;
- if (resultSize != sizes.SecurityTrailer)
- {
- forceCopy = true;
- Buffer.BlockCopy(output, securityBuffer[1].offset, output, 4 + resultSize, securityBuffer[1].size);
- }
-
- resultSize += securityBuffer[1].size;
- if (securityBuffer[2].size != 0 && (forceCopy || resultSize != (count + sizes.SecurityTrailer)))
- {
- Buffer.BlockCopy(output, securityBuffer[2].offset, output, 4 + resultSize, securityBuffer[2].size);
- }
-
- resultSize += securityBuffer[2].size;
+ int resultSize = NegoState.Encrypt(
+ _securityContext,
+ buffer,
+ offset,
+ count,
+ IsConfidentialityFlag,
+ IsNTLM,
+ ref output,
+ sequenceNumber);
unchecked
{
@@ -668,44 +500,7 @@ internal int Decrypt(byte[] payload, int offset, int count, out int newOffset, u
throw new ArgumentOutOfRangeException("count");
}
- if (IsNTLM)
- {
- return DecryptNtlm(payload, offset, count, out newOffset, expectedSeqNumber);
- }
-
- //
- // Kerberos and up
- //
- var securityBuffer = new SecurityBuffer[2];
- securityBuffer[0] = new SecurityBuffer(payload, offset, count, SecurityBufferType.Stream);
- securityBuffer[1] = new SecurityBuffer(0, SecurityBufferType.Data);
-
- int errorCode;
- if (IsConfidentialityFlag)
- {
- errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, _securityContext, securityBuffer, expectedSeqNumber);
- }
- else
- {
- errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, _securityContext, securityBuffer, expectedSeqNumber);
- }
-
- if (errorCode != 0)
- {
- if (GlobalLog.IsEnabled)
- {
- GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::Decrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
- }
- throw new Win32Exception(errorCode);
- }
-
- if (securityBuffer[1].type != SecurityBufferType.Data)
- {
- throw new InternalException();
- }
-
- newOffset = securityBuffer[1].offset;
- return securityBuffer[1].size;
+ return NegoState.Decrypt(_securityContext, payload, offset, count, IsConfidentialityFlag, IsNTLM, out newOffset, expectedSeqNumber);
}
private string GetClientSpecifiedSpn()
@@ -719,8 +514,7 @@ private string GetClientSpecifiedSpn()
Debug.Fail("NTAuthentication: Trying to get the client SPN before handshaking is done!");
}
- string spn = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, _securityContext,
- Interop.SspiCli.ContextAttribute.ClientSpecifiedSpn) as string;
+ string spn = NegoState.QueryContextClientSpecifiedSpn(_securityContext);
if (GlobalLog.IsEnabled)
{
@@ -743,40 +537,7 @@ private int DecryptNtlm(byte[] payload, int offset, int count, out int newOffset
throw new ArgumentOutOfRangeException("count");
}
- var securityBuffer = new SecurityBuffer[2];
- securityBuffer[0] = new SecurityBuffer(payload, offset, 16, SecurityBufferType.Token);
- securityBuffer[1] = new SecurityBuffer(payload, offset + 16, count - 16, SecurityBufferType.Data);
-
- int errorCode;
- SecurityBufferType realDataType = SecurityBufferType.Data;
-
- if (IsConfidentialityFlag)
- {
- errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, _securityContext, securityBuffer, expectedSeqNumber);
- }
- else
- {
- realDataType |= SecurityBufferType.ReadOnlyFlag;
- securityBuffer[1].type = realDataType;
- errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, _securityContext, securityBuffer, expectedSeqNumber);
- }
-
- if (errorCode != 0)
- {
- if (GlobalLog.IsEnabled)
- {
- GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(this) + "::Decrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
- }
- throw new Win32Exception(errorCode);
- }
-
- if (securityBuffer[1].type != realDataType)
- {
- throw new InternalException();
- }
-
- newOffset = securityBuffer[1].offset;
- return securityBuffer[1].size;
+ return NegoState.DecryptNtlm(_securityContext, payload, offset, count, IsConfidentialityFlag, out newOffset, expectedSeqNumber);
}
}
}
diff --git a/src/System.Net.Security/src/System/Net/NegotiationInfoClass.cs b/src/System.Net.Security/src/System/Net/NegotiationInfoClass.cs
new file mode 100644
index 000000000000..eaf5983cfa9d
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/NegotiationInfoClass.cs
@@ -0,0 +1,15 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+namespace System.Net
+{
+ // This class is used to determine if NTLM or
+ // Kerberos are used in the context of a Negotiate handshake
+ internal partial class NegotiationInfoClass
+ {
+ internal const string NTLM = "NTLM";
+ internal const string Kerberos = "Kerberos";
+ internal const string WDigest = "WDigest";
+ internal const string Negotiate = "Negotiate";
+ }
+}
diff --git a/src/System.Net.Security/src/System/Net/SecureProtocols/InternalNegoState.cs b/src/System.Net.Security/src/System/Net/SecureProtocols/InternalNegoState.cs
new file mode 100644
index 000000000000..beef946762f2
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/SecureProtocols/InternalNegoState.cs
@@ -0,0 +1,821 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System.Diagnostics;
+using System.IO;
+using System.Security;
+using System.Security.Principal;
+using System.Threading;
+using System.ComponentModel;
+using System.Security.Authentication;
+using System.Security.Authentication.ExtendedProtection;
+
+namespace System.Net.Security
+{
+ //
+ // The class maintains the state of the authentication process and the security context.
+ // It encapsulates security context and does the real work in authentication and
+ // user data encryption
+ //
+ internal partial class NegoState
+ {
+ private const int ERROR_TRUST_FAILURE = 1790; // Used to serialize protectionLevel or impersonationLevel mismatch error to the remote side.
+
+ private static readonly byte[] s_emptyMessage = new byte[0];
+ private static readonly AsyncCallback s_readCallback = new AsyncCallback(ReadCallback);
+ private static readonly AsyncCallback s_writeCallback = new AsyncCallback(WriteCallback);
+
+ private Stream _innerStream;
+ private bool _leaveStreamOpen;
+
+ private Exception _exception;
+
+ private StreamFramer _framer;
+ private NTAuthentication _context;
+
+ private int _nestedAuth;
+
+ internal const int MaxReadFrameSize = 64 * 1024;
+ internal const int MaxWriteDataSize = 63 * 1024; // 1k for the framing and trailer that is always less as per SSPI.
+
+ private bool _canRetryAuthentication;
+ private ProtectionLevel _expectedProtectionLevel;
+ private TokenImpersonationLevel _expectedImpersonationLevel;
+ private uint _writeSequenceNumber;
+ private uint _readSequenceNumber;
+
+ private ExtendedProtectionPolicy _extendedProtectionPolicy;
+
+ // SSPI does not send a server ack on successful auth.
+ // This is a state variable used to gracefully handle auth confirmation.
+ private bool _remoteOk = false;
+
+ internal NegoState(Stream innerStream, bool leaveStreamOpen)
+ {
+ if (innerStream == null)
+ {
+ throw new ArgumentNullException("stream");
+ }
+
+ _innerStream = innerStream;
+ _leaveStreamOpen = leaveStreamOpen;
+ }
+
+ internal static string DefaultPackage
+ {
+ get
+ {
+ return NegotiationInfoClass.Negotiate;
+ }
+ }
+
+ internal void ValidateCreateContext(
+ string package,
+ NetworkCredential credential,
+ string servicePrincipalName,
+ ExtendedProtectionPolicy policy,
+ ProtectionLevel protectionLevel,
+ TokenImpersonationLevel impersonationLevel)
+ {
+ if (policy != null)
+ {
+ // One of these must be set if EP is turned on
+ if (policy.CustomChannelBinding == null && policy.CustomServiceNames == null)
+ {
+ throw new ArgumentException(SR.net_auth_must_specify_extended_protection_scheme, "policy");
+ }
+
+ _extendedProtectionPolicy = policy;
+ }
+ else
+ {
+ _extendedProtectionPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never);
+ }
+
+ ValidateCreateContext(package, true, credential, servicePrincipalName, _extendedProtectionPolicy.CustomChannelBinding, protectionLevel, impersonationLevel);
+ }
+
+ internal void ValidateCreateContext(
+ string package,
+ bool isServer,
+ NetworkCredential credential,
+ string servicePrincipalName,
+ ChannelBinding channelBinding,
+ ProtectionLevel protectionLevel,
+ TokenImpersonationLevel impersonationLevel)
+ {
+ if (_exception != null && !_canRetryAuthentication)
+ {
+ throw _exception;
+ }
+
+ if (_context != null && _context.IsValidContext)
+ {
+ throw new InvalidOperationException(SR.net_auth_reauth);
+ }
+
+ if (credential == null)
+ {
+ throw new ArgumentNullException("credential");
+ }
+
+ if (servicePrincipalName == null)
+ {
+ throw new ArgumentNullException("servicePrincipalName");
+ }
+
+ ValidateImpersonationLevel(impersonationLevel);
+
+ if (_context != null && IsServer != isServer)
+ {
+ throw new InvalidOperationException(SR.net_auth_client_server);
+ }
+
+ _exception = null;
+ _remoteOk = false;
+ _framer = new StreamFramer(_innerStream);
+ _framer.WriteHeader.MessageId = FrameHeader.HandshakeId;
+
+ _expectedProtectionLevel = protectionLevel;
+ _expectedImpersonationLevel = isServer ? impersonationLevel : TokenImpersonationLevel.None;
+ _writeSequenceNumber = 0;
+ _readSequenceNumber = 0;
+
+ ContextFlagsPal flags = ContextFlagsPal.Connection;
+
+ // A workaround for the client when talking to Win9x on the server side.
+ if (protectionLevel == ProtectionLevel.None && !isServer)
+ {
+ package = NegotiationInfoClass.NTLM;
+ }
+ else if (protectionLevel == ProtectionLevel.EncryptAndSign)
+ {
+ flags |= ContextFlagsPal.Confidentiality;
+ }
+ else if (protectionLevel == ProtectionLevel.Sign)
+ {
+ // Assuming user expects NT4 SP4 and above.
+ flags |= (ContextFlagsPal.ReplayDetect | ContextFlagsPal.SequenceDetect | ContextFlagsPal.InitIntegrity);
+ }
+
+ if (isServer)
+ {
+ if (_extendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported)
+ {
+ flags |= ContextFlagsPal.AllowMissingBindings;
+ }
+
+ if (_extendedProtectionPolicy.PolicyEnforcement != PolicyEnforcement.Never &&
+ _extendedProtectionPolicy.ProtectionScenario == ProtectionScenario.TrustedProxy)
+ {
+ flags |= ContextFlagsPal.ProxyBindings;
+ }
+ }
+ else
+ {
+ // Server side should not request any of these flags.
+ if (protectionLevel != ProtectionLevel.None)
+ {
+ flags |= ContextFlagsPal.MutualAuth;
+ }
+
+ if (impersonationLevel == TokenImpersonationLevel.Identification)
+ {
+ flags |= ContextFlagsPal.InitIdentify;
+ }
+
+ if (impersonationLevel == TokenImpersonationLevel.Delegation)
+ {
+ flags |= ContextFlagsPal.Delegate;
+ }
+ }
+
+ _canRetryAuthentication = false;
+
+ try
+ {
+ _context = new NTAuthentication(isServer, package, credential, servicePrincipalName, flags, channelBinding);
+ }
+ catch (Exception e)
+ {
+ throw new AuthenticationException(SR.net_auth_SSPI, e);
+ }
+ }
+
+ private Exception SetException(Exception e)
+ {
+ if (_exception == null || !(_exception is ObjectDisposedException))
+ {
+ _exception = e;
+ }
+
+ if (_exception != null && _context != null)
+ {
+ _context.CloseContext();
+ }
+
+ return _exception;
+ }
+
+ internal bool IsAuthenticated
+ {
+ get
+ {
+ return _context != null && HandshakeComplete && _exception == null && _remoteOk;
+ }
+ }
+
+ internal bool IsMutuallyAuthenticated
+ {
+ get
+ {
+ if (!IsAuthenticated)
+ {
+ return false;
+ }
+
+ // Suppressing for NTLM since SSPI does not return correct value in the context flags.
+ if (_context.IsNTLM)
+ {
+ return false;
+ }
+
+ return _context.IsMutualAuthFlag;
+ }
+ }
+
+ internal bool IsEncrypted
+ {
+ get
+ {
+ return IsAuthenticated && _context.IsConfidentialityFlag;
+ }
+ }
+
+ internal bool IsSigned
+ {
+ get
+ {
+ return IsAuthenticated && (_context.IsIntegrityFlag || _context.IsConfidentialityFlag);
+ }
+ }
+
+ internal bool IsServer
+ {
+ get
+ {
+ return _context != null && _context.IsServer;
+ }
+ }
+
+ internal bool CanGetSecureStream
+ {
+ get
+ {
+ return (_context.IsConfidentialityFlag || _context.IsIntegrityFlag);
+ }
+ }
+
+ internal TokenImpersonationLevel AllowedImpersonation
+ {
+ get
+ {
+ CheckThrow(true);
+ return PrivateImpersonationLevel;
+ }
+ }
+
+ private TokenImpersonationLevel PrivateImpersonationLevel
+ {
+ get
+ {
+ // We should suppress the delegate flag in NTLM case.
+ return (_context.IsDelegationFlag && _context.ProtocolName != NegotiationInfoClass.NTLM) ? TokenImpersonationLevel.Delegation
+ : _context.IsIdentifyFlag ? TokenImpersonationLevel.Identification
+ : TokenImpersonationLevel.Impersonation;
+ }
+ }
+
+ private bool HandshakeComplete
+ {
+ get
+ {
+ return _context.IsCompleted && _context.IsValidContext;
+ }
+ }
+
+ internal void CheckThrow(bool authSucessCheck)
+ {
+ if (_exception != null)
+ {
+ throw _exception;
+ }
+
+ if (authSucessCheck && !IsAuthenticated)
+ {
+ throw new InvalidOperationException(SR.net_auth_noauth);
+ }
+ }
+
+ //
+ // This is to not depend on GC&SafeHandle class if the context is not needed anymore.
+ //
+ internal void Close()
+ {
+ // Mark this instance as disposed.
+ _exception = new ObjectDisposedException("NegotiateStream");
+ if (_context != null)
+ {
+ _context.CloseContext();
+ }
+ }
+
+ internal void ProcessAuthentication(LazyAsyncResult lazyResult)
+ {
+ CheckThrow(false);
+ if (Interlocked.Exchange(ref _nestedAuth, 1) == 1)
+ {
+ throw new InvalidOperationException(SR.Format(SR.net_io_invalidnestedcall, lazyResult == null ? "BeginAuthenticate" : "Authenticate", "authenticate"));
+ }
+
+ try
+ {
+ if (_context.IsServer)
+ {
+ // Listen for a client blob.
+ StartReceiveBlob(lazyResult);
+ }
+ else
+ {
+ // Start with the first blob.
+ StartSendBlob(null, lazyResult);
+ }
+ }
+ catch (Exception e)
+ {
+ // Round-trip it through SetException().
+ e = SetException(e);
+ throw;
+ }
+ finally
+ {
+ if (lazyResult == null || _exception != null)
+ {
+ _nestedAuth = 0;
+ }
+ }
+ }
+
+ internal void EndProcessAuthentication(IAsyncResult result)
+ {
+ if (result == null)
+ {
+ throw new ArgumentNullException("asyncResult");
+ }
+
+ LazyAsyncResult lazyResult = result as LazyAsyncResult;
+ if (lazyResult == null)
+ {
+ throw new ArgumentException(SR.Format(SR.net_io_async_result, result.GetType().FullName), "asyncResult");
+ }
+
+ if (Interlocked.Exchange(ref _nestedAuth, 0) == 0)
+ {
+ throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndAuthenticate"));
+ }
+
+ // No "artificial" timeouts implemented so far, InnerStream controls that.
+ lazyResult.InternalWaitForCompletion();
+
+ Exception e = lazyResult.Result as Exception;
+
+ if (e != null)
+ {
+ // Round-trip it through the SetException().
+ e = SetException(e);
+ throw e;
+ }
+ }
+
+ private bool CheckSpn()
+ {
+ if (_context.IsKerberos)
+ {
+ return true;
+ }
+
+ if (_extendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Never ||
+ _extendedProtectionPolicy.CustomServiceNames == null)
+ {
+ return true;
+ }
+
+ string clientSpn = _context.ClientSpecifiedSpn;
+
+ if (String.IsNullOrEmpty(clientSpn))
+ {
+ if (_extendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ return _extendedProtectionPolicy.CustomServiceNames.Contains(clientSpn);
+ }
+
+ return false;
+ }
+
+ //
+ // Client side starts here, but server also loops through this method.
+ //
+ private void StartSendBlob(byte[] message, LazyAsyncResult lazyResult)
+ {
+ Exception exception = null;
+ if (message != s_emptyMessage)
+ {
+ message = GetOutgoingBlob(message, ref exception);
+ }
+
+ if (exception != null)
+ {
+ // Signal remote side on a failed attempt.
+ StartSendAuthResetSignal(lazyResult, message, exception);
+ return;
+ }
+
+ if (HandshakeComplete)
+ {
+ if (_context.IsServer && !CheckSpn())
+ {
+ exception = new AuthenticationException(SR.net_auth_bad_client_creds_or_target_mismatch);
+ int statusCode = ERROR_TRUST_FAILURE;
+ message = new byte[8]; //sizeof(long)
+
+ for (int i = message.Length - 1; i >= 0; --i)
+ {
+ message[i] = (byte)(statusCode & 0xFF);
+ statusCode = (int)((uint)statusCode >> 8);
+ }
+
+ StartSendAuthResetSignal(lazyResult, message, exception);
+ return;
+ }
+
+ if (PrivateImpersonationLevel < _expectedImpersonationLevel)
+ {
+ exception = new AuthenticationException(SR.Format(SR.net_auth_context_expectation, _expectedImpersonationLevel.ToString(), PrivateImpersonationLevel.ToString()));
+ int statusCode = ERROR_TRUST_FAILURE;
+ message = new byte[8]; //sizeof(long)
+
+ for (int i = message.Length - 1; i >= 0; --i)
+ {
+ message[i] = (byte)(statusCode & 0xFF);
+ statusCode = (int)((uint)statusCode >> 8);
+ }
+
+ StartSendAuthResetSignal(lazyResult, message, exception);
+ return;
+ }
+
+ ProtectionLevel result = _context.IsConfidentialityFlag ? ProtectionLevel.EncryptAndSign : _context.IsIntegrityFlag ? ProtectionLevel.Sign : ProtectionLevel.None;
+
+ if (result < _expectedProtectionLevel)
+ {
+ exception = new AuthenticationException(SR.Format(SR.net_auth_context_expectation, result.ToString(), _expectedProtectionLevel.ToString()));
+ int statusCode = ERROR_TRUST_FAILURE;
+ message = new byte[8]; //sizeof(long)
+
+ for (int i = message.Length - 1; i >= 0; --i)
+ {
+ message[i] = (byte)(statusCode & 0xFF);
+ statusCode = (int)((uint)statusCode >> 8);
+ }
+
+ StartSendAuthResetSignal(lazyResult, message, exception);
+ return;
+ }
+
+ // Signal remote party that we are done
+ _framer.WriteHeader.MessageId = FrameHeader.HandshakeDoneId;
+ if (_context.IsServer)
+ {
+ // Server may complete now because client SSPI would not complain at this point.
+ _remoteOk = true;
+
+ // However the client will wait for server to send this ACK
+ //Force signaling server OK to the client
+ if (message == null)
+ {
+ message = s_emptyMessage;
+ }
+ }
+ }
+ else if (message == null || message == s_emptyMessage)
+ {
+ throw new InternalException();
+ }
+
+ if (message != null)
+ {
+ //even if we are completed, there could be a blob for sending.
+ if (lazyResult == null)
+ {
+ _framer.WriteMessage(message);
+ }
+ else
+ {
+ IAsyncResult ar = _framer.BeginWriteMessage(message, s_writeCallback, lazyResult);
+ if (!ar.CompletedSynchronously)
+ {
+ return;
+ }
+ _framer.EndWriteMessage(ar);
+ }
+ }
+ CheckCompletionBeforeNextReceive(lazyResult);
+ }
+
+ //
+ // This will check and logically complete the auth handshake.
+ //
+ private void CheckCompletionBeforeNextReceive(LazyAsyncResult lazyResult)
+ {
+ if (HandshakeComplete && _remoteOk)
+ {
+ // We are done with success.
+ if (lazyResult != null)
+ {
+ lazyResult.InvokeCallback();
+ }
+
+ return;
+ }
+
+ StartReceiveBlob(lazyResult);
+ }
+
+ //
+ // Server side starts here, but client also loops through this method.
+ //
+ private void StartReceiveBlob(LazyAsyncResult lazyResult)
+ {
+ byte[] message;
+ if (lazyResult == null)
+ {
+ message = _framer.ReadMessage();
+ }
+ else
+ {
+ IAsyncResult ar = _framer.BeginReadMessage(s_readCallback, lazyResult);
+ if (!ar.CompletedSynchronously)
+ {
+ return;
+ }
+
+ message = _framer.EndReadMessage(ar);
+ }
+
+ ProcessReceivedBlob(message, lazyResult);
+ }
+
+ private void ProcessReceivedBlob(byte[] message, LazyAsyncResult lazyResult)
+ {
+ // This is an EOF otherwise we would get at least *empty* message but not a null one.
+ if (message == null)
+ {
+ throw new AuthenticationException(SR.net_auth_eof, null);
+ }
+
+ // Process Header information.
+ if (_framer.ReadHeader.MessageId == FrameHeader.HandshakeErrId)
+ {
+ if (message.Length >= 8) // sizeof(long)
+ {
+ // Try to recover remote win32 Exception.
+ long error = 0;
+ for (int i = 0; i < 8; ++i)
+ {
+ error = (error << 8) + message[i];
+ }
+
+ ThrowCredentialException(error);
+ }
+
+ throw new AuthenticationException(SR.net_auth_alert, null);
+ }
+
+ if (_framer.ReadHeader.MessageId == FrameHeader.HandshakeDoneId)
+ {
+ _remoteOk = true;
+ }
+ else if (_framer.ReadHeader.MessageId != FrameHeader.HandshakeId)
+ {
+ throw new AuthenticationException(SR.Format(SR.net_io_header_id, "MessageId", _framer.ReadHeader.MessageId, FrameHeader.HandshakeId), null);
+ }
+
+ CheckCompletionBeforeNextSend(message, lazyResult);
+ }
+
+ //
+ // This will check and logically complete the auth handshake.
+ //
+ private void CheckCompletionBeforeNextSend(byte[] message, LazyAsyncResult lazyResult)
+ {
+ //If we are done don't go into send.
+ if (HandshakeComplete)
+ {
+ if (!_remoteOk)
+ {
+ throw new AuthenticationException(SR.Format(SR.net_io_header_id, "MessageId", _framer.ReadHeader.MessageId, FrameHeader.HandshakeDoneId), null);
+ }
+ if (lazyResult != null)
+ {
+ lazyResult.InvokeCallback();
+ }
+
+ return;
+ }
+
+ // Not yet done, get a new blob and send it if any.
+ StartSendBlob(message, lazyResult);
+ }
+
+ //
+ // This is to reset auth state on the remote side.
+ // If this write succeeds we will allow auth retrying.
+ //
+ private void StartSendAuthResetSignal(LazyAsyncResult lazyResult, byte[] message, Exception exception)
+ {
+ _framer.WriteHeader.MessageId = FrameHeader.HandshakeErrId;
+
+ if (IsLogonDeniedException(exception))
+ {
+ if (IsServer)
+ {
+ exception = new InvalidCredentialException(SR.net_auth_bad_client_creds, exception);
+ }
+ else
+ {
+ exception = new InvalidCredentialException(SR.net_auth_bad_client_creds_or_target_mismatch, exception);
+ }
+ }
+
+ if (!(exception is AuthenticationException))
+ {
+ exception = new AuthenticationException(SR.net_auth_SSPI, exception);
+ }
+
+ if (lazyResult == null)
+ {
+ _framer.WriteMessage(message);
+ }
+ else
+ {
+ lazyResult.Result = exception;
+ IAsyncResult ar = _framer.BeginWriteMessage(message, s_writeCallback, lazyResult);
+ if (!ar.CompletedSynchronously)
+ {
+ return;
+ }
+
+ _framer.EndWriteMessage(ar);
+ }
+
+ _canRetryAuthentication = true;
+ throw exception;
+ }
+
+ private static void WriteCallback(IAsyncResult transportResult)
+ {
+ if ((transportResult.AsyncState is LazyAsyncResult))
+ {
+ if (GlobalLog.IsEnabled)
+ {
+ GlobalLog.Assert("WriteCallback|State type is wrong, expected LazyAsyncResult.");
+ }
+ Debug.Fail("WriteCallback|State type is wrong, expected LazyAsyncResult.");
+ }
+
+ if (transportResult.CompletedSynchronously)
+ {
+ return;
+ }
+
+ LazyAsyncResult lazyResult = (LazyAsyncResult)transportResult.AsyncState;
+
+ // Async completion.
+ try
+ {
+ NegoState authState = (NegoState)lazyResult.AsyncObject;
+ authState._framer.EndWriteMessage(transportResult);
+
+ // Special case for an error notification.
+ if (lazyResult.Result is Exception)
+ {
+ authState._canRetryAuthentication = true;
+ throw (Exception)lazyResult.Result;
+ }
+
+ authState.CheckCompletionBeforeNextReceive(lazyResult);
+ }
+ catch (Exception e)
+ {
+ if (lazyResult.InternalPeekCompleted)
+ {
+ // This will throw on a worker thread.
+ throw;
+ }
+
+ lazyResult.InvokeCallback(e);
+ }
+ }
+
+ private static void ReadCallback(IAsyncResult transportResult)
+ {
+ if ((transportResult.AsyncState is LazyAsyncResult))
+ {
+ if (GlobalLog.IsEnabled)
+ {
+ GlobalLog.Assert("ReadCallback|State type is wrong, expected LazyAsyncResult.");
+ }
+ Debug.Fail("ReadCallback|State type is wrong, expected LazyAsyncResult.");
+ }
+
+ if (transportResult.CompletedSynchronously)
+ {
+ return;
+ }
+
+ LazyAsyncResult lazyResult = (LazyAsyncResult)transportResult.AsyncState;
+
+ // Async completion.
+ try
+ {
+ NegoState authState = (NegoState)lazyResult.AsyncObject;
+ byte[] message = authState._framer.EndReadMessage(transportResult);
+ authState.ProcessReceivedBlob(message, lazyResult);
+ }
+ catch (Exception e)
+ {
+ if (lazyResult.InternalPeekCompleted)
+ {
+ // This will throw on a worker thread.
+ throw;
+ }
+
+ lazyResult.InvokeCallback(e);
+ }
+ }
+
+ internal static bool IsError(SecurityStatusPal status)
+ {
+ return ((int)status >= (int)SecurityStatusPal.OutOfMemory);
+ }
+
+ private unsafe byte[] GetOutgoingBlob(byte[] incomingBlob, ref Exception e)
+ {
+ SecurityStatusPal statusCode;
+ byte[] message = _context.GetOutgoingBlob(incomingBlob, false, out statusCode);
+
+ if (IsError(statusCode))
+ {
+ e = CreateExceptionFromError(statusCode);
+ uint error = (uint)e.HResult;
+
+ message = new byte[8]; //sizeof(long)
+ for (int i = message.Length - 1; i >= 0; --i)
+ {
+ message[i] = (byte)(error & 0xFF);
+ error = (error >> 8);
+ }
+ }
+
+ if (message != null && message.Length == 0)
+ {
+ message = s_emptyMessage;
+ }
+
+ return message;
+ }
+
+ internal int EncryptData(byte[] buffer, int offset, int count, ref byte[] outBuffer)
+ {
+ CheckThrow(true);
+
+ // SSPI seems to ignore this sequence number.
+ ++_writeSequenceNumber;
+ return _context.Encrypt(buffer, offset, count, ref outBuffer, _writeSequenceNumber);
+ }
+
+ internal int DecryptData(byte[] buffer, int offset, int count, out int newOffset)
+ {
+ CheckThrow(true);
+
+ // SSPI seems to ignore this sequence number.
+ ++_readSequenceNumber;
+ return _context.Decrypt(buffer, offset, count, out newOffset, _readSequenceNumber);
+ }
+ }
+}
diff --git a/src/System.Net.Security/src/System/Net/SecureProtocols/NegoState.Unix.cs b/src/System.Net.Security/src/System/Net/SecureProtocols/NegoState.Unix.cs
index a98dc8fae706..d00d81a66711 100644
--- a/src/System.Net.Security/src/System/Net/SecureProtocols/NegoState.Unix.cs
+++ b/src/System.Net.Security/src/System/Net/SecureProtocols/NegoState.Unix.cs
@@ -6,8 +6,11 @@
using System.Security.Principal;
using System.Threading;
using System.ComponentModel;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
using System.Security.Authentication;
using System.Security.Authentication.ExtendedProtection;
+using Microsoft.Win32.SafeHandles;
namespace System.Net.Security
{
@@ -18,140 +21,346 @@ namespace System.Net.Security
//
// This is part of the NegotiateStream PAL.
//
- internal class NegoState
+ internal partial class NegoState
{
- internal const int MaxReadFrameSize = 64 * 1024;
- internal const int MaxWriteDataSize = 63 * 1024; // 1k for the framing and trailer that is always less as per SSPI.
+ private const int LogonDenied = unchecked((int)0x8009030C);
+
+ private class NNSProtocolException : Exception
+ {
+ internal static readonly Exception Instance;
+
+ // MS-NNS Protocol requires a Windows error code to be
+ // passed back. Hence, we always use NTE_FAIL
+ private const int NTE_FAIL = unchecked((int)0x80090020);
+
+ static NNSProtocolException()
+ {
+ Instance = new NNSProtocolException();
+ }
+
+ private NNSProtocolException() : base()
+ {
+ HResult = NTE_FAIL;
+ }
+ }
+
+
+ internal IIdentity GetIdentity()
+ {
+ Debug.Assert(!_context.IsServer, "GetIdentity: Server is not supported");
+
+ string name = _context.Spn;
+ string protocol = _context.ProtocolName;
+
+ return new GenericIdentity(name, protocol);
- internal NegoState(Stream innerStream, bool leaveStreamOpen)
+ }
+
+ internal static string QueryContextAssociatedName(SafeDeleteContext securityContext)
{
throw new PlatformNotSupportedException();
}
- internal static string DefaultPackage
+ internal static string QueryContextAuthenticationPackage(SafeDeleteContext securityContext)
{
- get
- {
- throw new PlatformNotSupportedException();
- }
+ return NegotiationInfoClass.Kerberos;
}
- internal void ValidateCreateContext(string package,
- NetworkCredential credential,
- string servicePrincipalName,
- ExtendedProtectionPolicy policy,
- ProtectionLevel protectionLevel,
- TokenImpersonationLevel impersonationLevel)
+ internal static object QueryContextSizes(SafeDeleteContext securityContext)
{
- throw new PlatformNotSupportedException();
+ // This return value is never used
+ return null;
+ }
+
+ internal static int QueryMaxTokenSize(string package)
+ {
+ // The value is unused in non-Windows code paths
+ return 0;
}
- internal void ValidateCreateContext(
- string package,
- bool isServer,
- NetworkCredential credential,
- string servicePrincipalName,
- ChannelBinding channelBinding,
- ProtectionLevel protectionLevel,
- TokenImpersonationLevel impersonationLevel
- )
+ internal static string QueryContextClientSpecifiedSpn(SafeDeleteContext securityContext)
{
throw new PlatformNotSupportedException();
}
- internal bool IsAuthenticated
+ internal static SafeFreeCredentials AcquireDefaultCredential(string package, bool isServer)
{
- get
- {
- throw new PlatformNotSupportedException();
- }
+ return AcquireCredentialsHandle(package, isServer, new NetworkCredential(string.Empty, string.Empty, string.Empty));
}
- internal bool IsMutuallyAuthenticated
+ internal static SafeFreeCredentials AcquireCredentialsHandle(string package, bool isServer, NetworkCredential credential)
{
- get
+ if (isServer)
{
- throw new PlatformNotSupportedException();
+ throw new NotImplementedException("AcquireCredentialsHandle: Server is not yet supported");
}
- }
- internal bool IsEncrypted
- {
- get
+ SafeFreeCredentials outCredential;
+ bool isNtlm = string.Equals(package, NegotiationInfoClass.NTLM);
+
+ if (isNtlm)
{
- throw new PlatformNotSupportedException();
+ throw new NotImplementedException("AcquireCredentialsHandle: NTLM is not yet supported");
}
- }
- internal bool IsSigned
- {
- get
+ if (string.IsNullOrEmpty(credential.UserName) || string.IsNullOrEmpty(credential.Password))
{
- throw new PlatformNotSupportedException();
+ // In client case, equivalent of default credentials is to use previous,
+ // cached Kerberos TGT to get service-specific ticket.
+ outCredential = new SafeFreeNegoCredentials(string.Empty, string.Empty, string.Empty);
}
+ else
+ {
+ outCredential = new SafeFreeNegoCredentials(credential.UserName, credential.Password, credential.Domain);
+ }
+ return outCredential;
+
}
- internal bool IsServer
+ internal static SecurityStatusPal InitializeSecurityContext(
+ SafeFreeCredentials credentialsHandle,
+ ref SafeDeleteContext securityContext,
+ string spn,
+ ContextFlagsPal requestedContextFlags,
+ SecurityBuffer[] inSecurityBufferArray,
+ SecurityBuffer outSecurityBuffer,
+ ref ContextFlagsPal contextFlags)
{
- get
+ // TODO (Issue #3718): The second buffer can contain a channel binding which is not supported
+ if ((null != inSecurityBufferArray) && (inSecurityBufferArray.Length > 1))
{
- throw new PlatformNotSupportedException();
+ throw new PlatformNotSupportedException("No support for channel binding on non-Windows");
}
+
+ return EstablishSecurityContext(
+ (SafeFreeNegoCredentials)credentialsHandle,
+ ref securityContext,
+ false,
+ spn,
+ requestedContextFlags,
+ ((inSecurityBufferArray != null) ? inSecurityBufferArray[0] : null),
+ outSecurityBuffer,
+ ref contextFlags);
+ }
+
+ internal static SecurityStatusPal CompleteAuthToken(
+ ref SafeDeleteContext securityContext,
+ SecurityBuffer[] inSecurityBufferArray)
+ {
+ return SecurityStatusPal.OK;
+ }
+
+ internal static SecurityStatusPal AcceptSecurityContext(
+ SafeFreeCredentials credentialsHandle,
+ ref SafeDeleteContext securityContext,
+ ContextFlagsPal requestedContextFlags,
+ SecurityBuffer[] inSecurityBufferArray,
+ SecurityBuffer outSecurityBuffer,
+ ref ContextFlagsPal contextFlags)
+ {
+ throw new PlatformNotSupportedException();
}
- internal bool CanGetSecureStream
+ private static void ValidateImpersonationLevel(TokenImpersonationLevel impersonationLevel)
{
- get
+ if (impersonationLevel != TokenImpersonationLevel.Identification)
{
- throw new PlatformNotSupportedException();
+ throw new ArgumentOutOfRangeException("impersonationLevel", impersonationLevel.ToString(),
+ SR.net_auth_supported_impl_levels);
}
+
}
- internal TokenImpersonationLevel AllowedImpersonation
+ private static void ThrowCredentialException(long error)
{
- get
+ string message = SR.net_auth_alert;
+ if ((int)error == LogonDenied)
{
- throw new PlatformNotSupportedException();
+ message = SR.net_auth_bad_client_creds;
}
+
+ if ((int)error == NegoState.ERROR_TRUST_FAILURE)
+ {
+ message = SR.net_auth_context_expectation_remote;
+ }
+
+ throw new AuthenticationException(message, null);
}
- internal IIdentity GetIdentity()
+ private static bool IsLogonDeniedException(Exception exception)
{
- throw new PlatformNotSupportedException();
+ return exception.HResult == LogonDenied;
}
- internal void CheckThrow(bool authSucessCheck)
+ internal static Exception CreateExceptionFromError(SecurityStatusPal statusCode)
{
- throw new PlatformNotSupportedException();
+ return NNSProtocolException.Instance;
}
- //
- // This is to not depend on GC&SafeHandle class if the context is not needed anymore.
- //
- internal void Close()
+ internal static int Encrypt(
+ SafeDeleteContext securityContext,
+ byte[] buffer,
+ int offset,
+ int count,
+ bool isConfidential,
+ bool isNtlm,
+ ref byte[] output,
+ uint sequenceNumber)
{
- throw new PlatformNotSupportedException();
+ Debug.Assert(!isNtlm, "Encrypt: NTLM is not yet supported");
+ SafeDeleteNegoContext gssContext = securityContext as SafeDeleteNegoContext;
+ byte[] tempOutput;
+ Interop.NetSecurity.Encrypt(gssContext.GssContext, isConfidential, buffer, offset, count, out tempOutput);
+
+ // Create space for prefixing with the length
+ const int prefixLength = 4;
+ output = new byte[tempOutput.Length + prefixLength];
+ Array.Copy(tempOutput, 0, output, prefixLength, tempOutput.Length);
+ return tempOutput.Length;
}
- internal void ProcessAuthentication(LazyAsyncResult lazyResult)
+ internal static int Decrypt(
+ SafeDeleteContext securityContext,
+ byte[] buffer,
+ int offset,
+ int count,
+ bool isConfidential,
+ bool isNtlm,
+ out int newOffset,
+ uint sequenceNumber)
{
- throw new PlatformNotSupportedException();
+ newOffset = offset;
+ return Interop.NetSecurity.Decrypt(((SafeDeleteNegoContext)securityContext).GssContext, buffer, offset, count);
}
- internal void EndProcessAuthentication(IAsyncResult result)
+ internal static int DecryptNtlm(
+ SafeDeleteContext securityContext,
+ byte[] buffer,
+ int offset,
+ int count,
+ bool isConfidential,
+ out int newOffset,
+ uint sequenceNumber)
{
- throw new PlatformNotSupportedException();
+ throw new NotImplementedException("DecryptNtlm: NTLM is not yet supported");
}
+ private static SecurityStatusPal EstablishSecurityContext(
+ SafeFreeNegoCredentials credential,
+ ref SafeDeleteContext context,
+ bool isNtlm,
+ string targetName,
+ ContextFlagsPal inFlags,
+ SecurityBuffer inputBuffer,
+ SecurityBuffer outputBuffer,
+ ref ContextFlagsPal outFlags)
+ {
+ Debug.Assert(!isNtlm, "EstablishSecurityContext: NTLM is not yet supported");
+
+ if (context == null)
+ {
+ context = new SafeDeleteNegoContext(credential, targetName);
+ }
+
+ SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
+ try
+ {
+ Interop.NetSecurity.GssFlags inputFlags = GetInteropGssFromContextFlagsPal(inFlags);
+ uint outputFlags;
+ SafeGssContextHandle contextHandle = negoContext.GssContext;
+
+ bool done = Interop.NetSecurity.EstablishSecurityContext(
+ ref contextHandle,
+ credential.GssCredential,
+ isNtlm,
+ negoContext.TargetName,
+ inputFlags,
+ ((inputBuffer != null) ? inputBuffer.token : null),
+ out outputBuffer.token,
+ out outputFlags);
- internal int EncryptData(byte[] buffer, int offset, int count, ref byte[] outBuffer)
+ Debug.Assert(outputBuffer.token != null, "Unexpected null buffer returned by GssApi");
+ outputBuffer.size = outputBuffer.token.Length;
+ outputBuffer.offset = 0;
+
+ outFlags = GetContextFlagsPalFromInteropGss((Interop.NetSecurity.GssFlags)outputFlags);
+
+ // Save the inner context handle for further calls to NetSecurity
+ if (null == negoContext.GssContext)
+ {
+ negoContext.SetGssContext(contextHandle);
+ }
+ return done ? SecurityStatusPal.CompleteNeeded : SecurityStatusPal.ContinueNeeded;
+ }
+ catch
+ {
+ return SecurityStatusPal.InternalError;
+ }
+ }
+
+ private static ContextFlagsPal GetContextFlagsPalFromInteropGss(Interop.NetSecurity.GssFlags gssFlags)
{
- throw new PlatformNotSupportedException();
+ ContextFlagsPal flags = ContextFlagsPal.Zero;
+ if ((gssFlags & Interop.NetSecurity.GssFlags.GSS_C_INTEG_FLAG) != 0)
+ {
+ flags |= (ContextFlagsPal.AcceptIntegrity | ContextFlagsPal.InitIntegrity);
+ }
+ if ((gssFlags & Interop.NetSecurity.GssFlags.GSS_C_CONF_FLAG) != 0)
+ {
+ flags |= ContextFlagsPal.Confidentiality;
+ }
+ if ((gssFlags & Interop.NetSecurity.GssFlags.GSS_C_IDENTIFY_FLAG) != 0)
+ {
+ flags |= ContextFlagsPal.InitIdentify;
+ }
+ if ((gssFlags & Interop.NetSecurity.GssFlags.GSS_C_MUTUAL_FLAG) != 0)
+ {
+ flags |= ContextFlagsPal.MutualAuth;
+ }
+ if ((gssFlags & Interop.NetSecurity.GssFlags.GSS_C_REPLAY_FLAG) != 0)
+ {
+ flags |= ContextFlagsPal.ReplayDetect;
+ }
+ if ((gssFlags & Interop.NetSecurity.GssFlags.GSS_C_SEQUENCE_FLAG) != 0)
+ {
+ flags |= ContextFlagsPal.SequenceDetect;
+ }
+ return flags;
}
- internal int DecryptData(byte[] buffer, int offset, int count, out int newOffset)
+ private static Interop.NetSecurity.GssFlags GetInteropGssFromContextFlagsPal(ContextFlagsPal flags)
{
- throw new PlatformNotSupportedException();
+ Interop.NetSecurity.GssFlags gssFlags = (Interop.NetSecurity.GssFlags)0;
+ if ((flags & ContextFlagsPal.AcceptIntegrity) != 0)
+ {
+ gssFlags |= Interop.NetSecurity.GssFlags.GSS_C_INTEG_FLAG;
+ }
+ if ((flags & ContextFlagsPal.Confidentiality) != 0)
+ {
+ gssFlags |= Interop.NetSecurity.GssFlags.GSS_C_CONF_FLAG;
+ }
+ if ((flags & ContextFlagsPal.InitIdentify) != 0)
+ {
+ gssFlags |= Interop.NetSecurity.GssFlags.GSS_C_IDENTIFY_FLAG;
+ }
+ if ((flags & ContextFlagsPal.InitIntegrity) != 0)
+ {
+ gssFlags |= Interop.NetSecurity.GssFlags.GSS_C_INTEG_FLAG;
+ }
+ if ((flags & ContextFlagsPal.MutualAuth) != 0)
+ {
+ gssFlags |= Interop.NetSecurity.GssFlags.GSS_C_MUTUAL_FLAG;
+ }
+ if ((flags & ContextFlagsPal.ReplayDetect) != 0)
+ {
+ gssFlags |= Interop.NetSecurity.GssFlags.GSS_C_REPLAY_FLAG;
+ }
+ if ((flags & ContextFlagsPal.SequenceDetect) != 0)
+ {
+ gssFlags |= Interop.NetSecurity.GssFlags.GSS_C_SEQUENCE_FLAG;
+ }
+ return gssFlags;
}
+
}
}
diff --git a/src/System.Net.Security/src/System/Net/SecureProtocols/NegoState.Windows.cs b/src/System.Net.Security/src/System/Net/SecureProtocols/NegoState.Windows.cs
index e07415ee00f9..da0b47514f45 100644
--- a/src/System.Net.Security/src/System/Net/SecureProtocols/NegoState.Windows.cs
+++ b/src/System.Net.Security/src/System/Net/SecureProtocols/NegoState.Windows.cs
@@ -1,7 +1,7 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
-using System.Diagnostics;
+using System.Globalization;
using System.IO;
using System.Security;
using System.Security.Principal;
@@ -13,869 +13,607 @@
namespace System.Net.Security
{
//
- // The class maintains the state of the authentication process and the security context.
- // It encapsulates security context and does the real work in authentication and
+ // The class does the real work in authentication and
// user data encryption with NEGO SSPI package.
//
// This is part of the NegotiateStream PAL.
//
- internal class NegoState
+ internal partial class NegoState
{
- private const int ERROR_TRUST_FAILURE = 1790; // Used to serialize protectionLevel or impersonationLevel mismatch error to the remote side.
-
- private static readonly byte[] s_emptyMessage = new byte[0];
- private static readonly AsyncCallback s_readCallback = new AsyncCallback(ReadCallback);
- private static readonly AsyncCallback s_writeCallback = new AsyncCallback(WriteCallback);
-
- private Stream _innerStream;
- private bool _leaveStreamOpen;
+ internal IIdentity GetIdentity()
+ {
+ CheckThrow(true);
- private Exception _exception;
+ IIdentity result = null;
+ string name = _context.IsServer ? _context.AssociatedName : _context.Spn;
+ string protocol = "NTLM";
- private StreamFramer _framer;
- private NTAuthentication _context;
+ protocol = _context.ProtocolName;
- private int _nestedAuth;
+ if (_context.IsServer)
+ {
+ SecurityContextTokenHandle token = null;
+ try
+ {
+ SecurityStatusPal status;
+ SafeDeleteContext securityContext = _context.GetContext(out status);
+ if (status != SecurityStatusPal.OK)
+ {
+ throw new Win32Exception((int)SslStreamPal.GetInteropFromSecurityStatusPal(status));
+ }
- internal const int MaxReadFrameSize = 64 * 1024;
- internal const int MaxWriteDataSize = 63 * 1024; // 1k for the framing and trailer that is always less as per SSPI.
+ // This will return a client token when conducted authentication on server side.
+ // This token can be used for impersonation. We use it to create a WindowsIdentity and hand it out to the server app.
+ Interop.SecurityStatus winStatus = (Interop.SecurityStatus)SSPIWrapper.QuerySecurityContextToken(
+ GlobalSSPI.SSPIAuth,
+ securityContext,
+ out token);
+ if (winStatus != Interop.SecurityStatus.OK)
+ {
+ throw new Win32Exception((int)winStatus);
+ }
+ string authtype = _context.ProtocolName;
- private bool _canRetryAuthentication;
- private ProtectionLevel _expectedProtectionLevel;
- private TokenImpersonationLevel _expectedImpersonationLevel;
- private uint _writeSequenceNumber;
- private uint _readSequenceNumber;
+ // TODO #5241:
+ // The following call was also specifying WindowsAccountType.Normal, true.
+ // WindowsIdentity.IsAuthenticated is no longer supported in CoreFX.
+ result = new WindowsIdentity(token.DangerousGetHandle(), authtype);
+ return result;
+ }
+ catch (SecurityException)
+ {
+ // Ignore and construct generic Identity if failed due to security problem.
+ }
+ finally
+ {
+ if (token != null)
+ {
+ token.Dispose();
+ }
+ }
+ }
- private ExtendedProtectionPolicy _extendedProtectionPolicy;
+ // On the client we don't have access to the remote side identity.
+ result = new GenericIdentity(name, protocol);
+ return result;
+ }
- // SSPI does not send a server ack on successful auth.
- // This is a state variable used to gracefully handle auth confirmation.
- private bool _remoteOk = false;
+ internal static string QueryContextAssociatedName(SafeDeleteContext securityContext)
+ {
+ return SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.Names) as string;
+ }
- internal NegoState(Stream innerStream, bool leaveStreamOpen)
+ internal static string QueryContextAuthenticationPackage(SafeDeleteContext securityContext)
{
- if (innerStream == null)
+ var negotiationInfoClass = SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.NegotiationInfo) as NegotiationInfoClass;
+ if (negotiationInfoClass != null)
{
- throw new ArgumentNullException("stream");
+ return negotiationInfoClass.AuthenticationPackage;
}
+ return null;
+ }
- _innerStream = innerStream;
- _leaveStreamOpen = leaveStreamOpen;
+ internal static int QueryMaxTokenSize(string package)
+ {
+ return SSPIWrapper.GetVerifyPackageInfo(GlobalSSPI.SSPIAuth, package, true).MaxToken;
}
- internal static string DefaultPackage
+ internal static string QueryContextClientSpecifiedSpn(SafeDeleteContext securityContext)
{
- get
- {
- return NegotiationInfoClass.Negotiate;
- }
+ return SSPIWrapper.QueryContextAttributes(GlobalSSPI.SSPIAuth, securityContext, Interop.SspiCli.ContextAttribute.ClientSpecifiedSpn) as string;
}
- internal void ValidateCreateContext(
- string package,
- NetworkCredential credential,
- string servicePrincipalName,
- ExtendedProtectionPolicy policy,
- ProtectionLevel protectionLevel,
- TokenImpersonationLevel impersonationLevel)
+ internal static SafeFreeCredentials AcquireDefaultCredential(string package, bool isServer)
{
- if (policy != null)
+ return SSPIWrapper.AcquireDefaultCredential(
+ GlobalSSPI.SSPIAuth,
+ package,
+ (isServer ? Interop.SspiCli.CredentialUse.Inbound : Interop.SspiCli.CredentialUse.Outbound));
+ }
+
+ internal static SafeFreeCredentials AcquireCredentialsHandle(string package, bool isServer, NetworkCredential credential)
+ {
+ unsafe
{
- // One of these must be set if EP is turned on
- if (policy.CustomChannelBinding == null && policy.CustomServiceNames == null)
+ SafeSspiAuthDataHandle authData = null;
+ try
{
- throw new ArgumentException(SR.net_auth_must_specify_extended_protection_scheme, "policy");
- }
+ Interop.SecurityStatus result = Interop.SspiCli.SspiEncodeStringsAsAuthIdentity(
+ credential.UserName, credential.Domain,
+ credential.Password, out authData);
- _extendedProtectionPolicy = policy;
- }
- else
- {
- _extendedProtectionPolicy = new ExtendedProtectionPolicy(PolicyEnforcement.Never);
- }
+ if (result != Interop.SecurityStatus.OK)
+ {
+ if (NetEventSource.Log.IsEnabled())
+ {
+ NetEventSource.PrintError(
+ NetEventSource.ComponentType.Security,
+ SR.Format(
+ SR.net_log_operation_failed_with_error,
+ "SspiEncodeStringsAsAuthIdentity()",
+ String.Format(CultureInfo.CurrentCulture, "0x{0:X}", (int)result)));
+ }
+
+ throw new Win32Exception((int)result);
+ }
- ValidateCreateContext(package, true, credential, servicePrincipalName, _extendedProtectionPolicy.CustomChannelBinding, protectionLevel, impersonationLevel);
+ return SSPIWrapper.AcquireCredentialsHandle(GlobalSSPI.SSPIAuth,
+ package, (isServer ? Interop.SspiCli.CredentialUse.Inbound : Interop.SspiCli.CredentialUse.Outbound), ref authData);
+ }
+ finally
+ {
+ if (authData != null)
+ {
+ authData.Dispose();
+ }
+ }
+ }
}
- internal void ValidateCreateContext(
- string package,
- bool isServer,
- NetworkCredential credential,
- string servicePrincipalName,
- ChannelBinding channelBinding,
- ProtectionLevel protectionLevel,
- TokenImpersonationLevel impersonationLevel)
+ internal static SecurityStatusPal InitializeSecurityContext(
+ SafeFreeCredentials credentialsHandle,
+ ref SafeDeleteContext securityContext,
+ string spn,
+ ContextFlagsPal requestedContextFlags,
+ SecurityBuffer[] inSecurityBufferArray,
+ SecurityBuffer outSecurityBuffer,
+ ref ContextFlagsPal contextFlags)
{
- if (_exception != null && !_canRetryAuthentication)
- {
- throw _exception;
- }
-
- if (_context != null && _context.IsValidContext)
- {
- throw new InvalidOperationException(SR.net_auth_reauth);
- }
+ Interop.SspiCli.ContextFlags outContextFlags = Interop.SspiCli.ContextFlags.Zero;
+ Interop.SecurityStatus winStatus = (Interop.SecurityStatus)SSPIWrapper.InitializeSecurityContext(
+ GlobalSSPI.SSPIAuth,
+ credentialsHandle,
+ ref securityContext,
+ spn,
+ GetInteropFromContextFlagsPal(requestedContextFlags),
+ Interop.SspiCli.Endianness.Network,
+ inSecurityBufferArray,
+ outSecurityBuffer,
+ ref outContextFlags);
+
+ contextFlags = GetContextFlagsPalFromInterop(outContextFlags);
+ return SslStreamPal.GetSecurityStatusPalFromInterop(winStatus);
+ }
- if (credential == null)
- {
- throw new ArgumentNullException("credential");
- }
+ internal static SecurityStatusPal CompleteAuthToken(
+ ref SafeDeleteContext securityContext,
+ SecurityBuffer[] inSecurityBufferArray)
+ {
+ Interop.SecurityStatus winStatus = (Interop.SecurityStatus)SSPIWrapper.CompleteAuthToken(
+ GlobalSSPI.SSPIAuth,
+ ref securityContext,
+ inSecurityBufferArray);
+ return SslStreamPal.GetSecurityStatusPalFromInterop(winStatus);
+ }
- if (servicePrincipalName == null)
- {
- throw new ArgumentNullException("servicePrincipalName");
- }
+ internal static SecurityStatusPal AcceptSecurityContext(
+ SafeFreeCredentials credentialsHandle,
+ ref SafeDeleteContext securityContext,
+ ContextFlagsPal requestedContextFlags,
+ SecurityBuffer[] inSecurityBufferArray,
+ SecurityBuffer outSecurityBuffer,
+ ref ContextFlagsPal contextFlags)
+ {
+ Interop.SspiCli.ContextFlags outContextFlags = Interop.SspiCli.ContextFlags.Zero;
+ Interop.SecurityStatus winStatus = (Interop.SecurityStatus)SSPIWrapper.AcceptSecurityContext(
+ GlobalSSPI.SSPIAuth,
+ credentialsHandle,
+ ref securityContext,
+ GetInteropFromContextFlagsPal(requestedContextFlags),
+ Interop.SspiCli.Endianness.Network,
+ inSecurityBufferArray,
+ outSecurityBuffer,
+ ref outContextFlags);
+
+ contextFlags = GetContextFlagsPalFromInterop(outContextFlags);
+ return SslStreamPal.GetSecurityStatusPalFromInterop(winStatus);
+ }
+ private static void ValidateImpersonationLevel(TokenImpersonationLevel impersonationLevel)
+ {
if (impersonationLevel != TokenImpersonationLevel.Identification &&
impersonationLevel != TokenImpersonationLevel.Impersonation &&
impersonationLevel != TokenImpersonationLevel.Delegation)
{
throw new ArgumentOutOfRangeException("impersonationLevel", impersonationLevel.ToString(), SR.net_auth_supported_impl_levels);
}
+ }
- if (_context != null && IsServer != isServer)
+ private static void ThrowCredentialException(long error)
+ {
+ Win32Exception e = new Win32Exception((int)error);
+
+ if (e.NativeErrorCode == (int)Interop.SecurityStatus.LogonDenied)
{
- throw new InvalidOperationException(SR.net_auth_client_server);
+ throw new InvalidCredentialException(SR.net_auth_bad_client_creds, e);
}
- _exception = null;
- _remoteOk = false;
- _framer = new StreamFramer(_innerStream);
- _framer.WriteHeader.MessageId = FrameHeader.HandshakeId;
+ if (e.NativeErrorCode == NegoState.ERROR_TRUST_FAILURE)
+ {
+ throw new AuthenticationException(SR.net_auth_context_expectation_remote, e);
+ }
- _expectedProtectionLevel = protectionLevel;
- _expectedImpersonationLevel = isServer ? impersonationLevel : TokenImpersonationLevel.None;
- _writeSequenceNumber = 0;
- _readSequenceNumber = 0;
+ throw new AuthenticationException(SR.net_auth_alert, e);
+ }
- Interop.SspiCli.ContextFlags flags = Interop.SspiCli.ContextFlags.Connection;
+ private static bool IsLogonDeniedException(Exception exception)
+ {
+ Win32Exception win32exception = exception as Win32Exception;
- // A workaround for the client when talking to Win9x on the server side.
- if (protectionLevel == ProtectionLevel.None && !isServer)
- {
- package = NegotiationInfoClass.NTLM;
- }
+ return (win32exception != null) && (win32exception.NativeErrorCode == (int)Interop.SecurityStatus.LogonDenied);
+ }
- else if (protectionLevel == ProtectionLevel.EncryptAndSign)
- {
- flags |= Interop.SspiCli.ContextFlags.Confidentiality;
- }
- else if (protectionLevel == ProtectionLevel.Sign)
- {
- // Assuming user expects NT4 SP4 and above.
- flags |= Interop.SspiCli.ContextFlags.ReplayDetect | Interop.SspiCli.ContextFlags.SequenceDetect | Interop.SspiCli.ContextFlags.InitIntegrity;
- }
+ internal static Exception CreateExceptionFromError(SecurityStatusPal statusCode)
+ {
+ return new Win32Exception((int)SslStreamPal.GetInteropFromSecurityStatusPal(statusCode));
+ }
+
+ internal static int Encrypt(
+ SafeDeleteContext securityContext,
+ byte[] buffer,
+ int offset,
+ int count,
+ bool isConfidential,
+ bool isNtlm,
+ ref byte[] output,
+ uint sequenceNumber)
+ {
+ SecSizes sizes = SSPIWrapper.QueryContextAttributes(
+ GlobalSSPI.SSPIAuth,
+ securityContext,
+ Interop.SspiCli.ContextAttribute.Sizes
+ ) as SecSizes;
- if (isServer)
+ try
{
- if (_extendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported)
- {
- flags |= Interop.SspiCli.ContextFlags.AllowMissingBindings;
- }
+ int maxCount = checked(Int32.MaxValue - 4 - sizes.BlockSize - sizes.SecurityTrailer);
- if (_extendedProtectionPolicy.PolicyEnforcement != PolicyEnforcement.Never &&
- _extendedProtectionPolicy.ProtectionScenario == ProtectionScenario.TrustedProxy)
+ if (count > maxCount || count < 0)
{
- flags |= Interop.SspiCli.ContextFlags.ProxyBindings;
+ throw new ArgumentOutOfRangeException("count", SR.Format(SR.net_io_out_range, maxCount));
}
}
- else
+ catch (Exception e)
{
- // Server side should not request any of these flags.
- if (protectionLevel != ProtectionLevel.None)
- {
- flags |= Interop.SspiCli.ContextFlags.MutualAuth;
- }
-
- if (impersonationLevel == TokenImpersonationLevel.Identification)
+ if (!ExceptionCheck.IsFatal(e) && GlobalLog.IsEnabled)
{
- flags |= Interop.SspiCli.ContextFlags.InitIdentify;
+ GlobalLog.Assert("NTAuthentication#" + LoggingHash.HashString(securityContext) + "::Encrypt", "Arguments out of range.");
}
- if (impersonationLevel == TokenImpersonationLevel.Delegation)
- {
- flags |= Interop.SspiCli.ContextFlags.Delegate;
- }
+ throw;
}
- _canRetryAuthentication = false;
-
- try
- {
- _context = new NTAuthentication(isServer, package, credential, servicePrincipalName, flags, channelBinding);
- }
- catch (Win32Exception e)
+ int resultSize = count + sizes.SecurityTrailer + sizes.BlockSize;
+ if (output == null || output.Length < resultSize + 4)
{
- throw new AuthenticationException(SR.net_auth_SSPI, e);
+ output = new byte[resultSize + 4];
}
- }
- private Exception SetException(Exception e)
- {
- if (_exception == null || !(_exception is ObjectDisposedException))
- {
- _exception = e;
- }
+ // Make a copy of user data for in-place encryption.
+ Buffer.BlockCopy(buffer, offset, output, 4 + sizes.SecurityTrailer, count);
- if (_exception != null && _context != null)
- {
- _context.CloseContext();
- }
-
- return _exception;
- }
+ // Prepare buffers TOKEN(signature), DATA and Padding.
+ var securityBuffer = new SecurityBuffer[3];
+ securityBuffer[0] = new SecurityBuffer(output, 4, sizes.SecurityTrailer, SecurityBufferType.Token);
+ securityBuffer[1] = new SecurityBuffer(output, 4 + sizes.SecurityTrailer, count, SecurityBufferType.Data);
+ securityBuffer[2] = new SecurityBuffer(output, 4 + sizes.SecurityTrailer + count, sizes.BlockSize, SecurityBufferType.Padding);
- internal bool IsAuthenticated
- {
- get
+ int errorCode;
+ if (isConfidential)
{
- return _context != null && HandshakeComplete && _exception == null && _remoteOk;
+ errorCode = SSPIWrapper.EncryptMessage(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
}
- }
-
- internal bool IsMutuallyAuthenticated
- {
- get
+ else
{
- if (!IsAuthenticated)
+ if (isNtlm)
{
- return false;
+ securityBuffer[1].type |= SecurityBufferType.ReadOnlyFlag;
}
- // Suppressing for NTLM since SSPI does not return correct value in the context flags.
- if (_context.IsNTLM)
+ errorCode = SSPIWrapper.MakeSignature(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, 0);
+ }
+
+ if (errorCode != 0)
+ {
+ if (GlobalLog.IsEnabled)
{
- return false;
+ GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(securityContext) + "::Encrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
}
-
- return _context.IsMutualAuthFlag;
+ throw new Win32Exception(errorCode);
}
- }
- internal bool IsEncrypted
- {
- get
+ // Compacting the result.
+ resultSize = securityBuffer[0].size;
+ bool forceCopy = false;
+ if (resultSize != sizes.SecurityTrailer)
{
- return IsAuthenticated && _context.IsConfidentialityFlag;
+ forceCopy = true;
+ Buffer.BlockCopy(output, securityBuffer[1].offset, output, 4 + resultSize, securityBuffer[1].size);
}
- }
- internal bool IsSigned
- {
- get
+ resultSize += securityBuffer[1].size;
+ if (securityBuffer[2].size != 0 && (forceCopy || resultSize != (count + sizes.SecurityTrailer)))
{
- return IsAuthenticated && (_context.IsIntegrityFlag || _context.IsConfidentialityFlag);
+ Buffer.BlockCopy(output, securityBuffer[2].offset, output, 4 + resultSize, securityBuffer[2].size);
}
+
+ resultSize += securityBuffer[2].size;
+ return resultSize;
}
- internal bool IsServer
+ internal static int Decrypt(
+ SafeDeleteContext securityContext,
+ byte[] buffer,
+ int offset,
+ int count,
+ bool isConfidential,
+ bool isNtlm,
+ out int newOffset,
+ uint sequenceNumber)
{
- get
+ if (isNtlm)
{
- return _context != null && _context.IsServer;
+ return DecryptNtlm(securityContext, buffer, offset, count, isConfidential, out newOffset, sequenceNumber);
}
- }
- internal bool CanGetSecureStream
- {
- get
+ //
+ // Kerberos and up
+ //
+ var securityBuffer = new SecurityBuffer[2];
+ securityBuffer[0] = new SecurityBuffer(buffer, offset, count, SecurityBufferType.Stream);
+ securityBuffer[1] = new SecurityBuffer(0, SecurityBufferType.Data);
+
+ int errorCode;
+ if (isConfidential)
{
- return (_context.IsConfidentialityFlag || _context.IsIntegrityFlag);
+ errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
}
- }
-
- internal TokenImpersonationLevel AllowedImpersonation
- {
- get
+ else
{
- CheckThrow(true);
- return PrivateImpersonationLevel;
+ errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
}
- }
- private TokenImpersonationLevel PrivateImpersonationLevel
- {
- get
+ if (errorCode != 0)
{
- // We should suppress the delegate flag in NTLM case.
- return (_context.IsDelegationFlag && _context.ProtocolName != NegotiationInfoClass.NTLM) ? TokenImpersonationLevel.Delegation
- : _context.IsIdentifyFlag ? TokenImpersonationLevel.Identification
- : TokenImpersonationLevel.Impersonation;
+ if (GlobalLog.IsEnabled)
+ {
+ GlobalLog.Print("NTAuthentication#"+ "::Decrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
+ }
+ throw new Win32Exception(errorCode);
}
- }
- private bool HandshakeComplete
- {
- get
+ if (securityBuffer[1].type != SecurityBufferType.Data)
{
- return _context.IsCompleted && _context.IsValidContext;
+ throw new InternalException();
}
+
+ newOffset = securityBuffer[1].offset;
+ return securityBuffer[1].size;
}
- internal IIdentity GetIdentity()
+ internal static int DecryptNtlm(
+ SafeDeleteContext securityContext,
+ byte[] buffer,
+ int offset,
+ int count,
+ bool isConfidential,
+ out int newOffset,
+ uint sequenceNumber)
{
- CheckThrow(true);
-
- IIdentity result = null;
- string name = _context.IsServer ? _context.AssociatedName : _context.Spn;
- string protocol = "NTLM";
+ var securityBuffer = new SecurityBuffer[2];
+ securityBuffer[0] = new SecurityBuffer(buffer, offset, 16, SecurityBufferType.Token);
+ securityBuffer[1] = new SecurityBuffer(buffer, offset + 16, count - 16, SecurityBufferType.Data);
- protocol = _context.ProtocolName;
+ int errorCode;
+ SecurityBufferType realDataType = SecurityBufferType.Data;
- if (_context.IsServer)
+ if (isConfidential)
{
- SecurityContextTokenHandle token = null;
- try
- {
- token = _context.GetContextToken();
- string authtype = _context.ProtocolName;
+ errorCode = SSPIWrapper.DecryptMessage(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
+ }
+ else
+ {
+ realDataType |= SecurityBufferType.ReadOnlyFlag;
+ securityBuffer[1].type = realDataType;
+ errorCode = SSPIWrapper.VerifySignature(GlobalSSPI.SSPIAuth, securityContext, securityBuffer, sequenceNumber);
+ }
- // TODO #5241:
- // The following call was also specifying WindowsAccountType.Normal, true.
- // WindowsIdentity.IsAuthenticated is no longer supported in CoreFX.
- result = new WindowsIdentity(token.DangerousGetHandle(), authtype);
- return result;
- }
- catch (SecurityException)
- {
- // Ignore and construct generic Identity if failed due to security problem.
- }
- finally
+ if (errorCode != 0)
+ {
+ if (GlobalLog.IsEnabled)
{
- if (token != null)
- {
- token.Dispose();
- }
+ GlobalLog.Print("NTAuthentication#" + LoggingHash.HashString(securityContext) + "::Decrypt() throw Error = " + errorCode.ToString("x", NumberFormatInfo.InvariantInfo));
}
+ throw new Win32Exception(errorCode);
}
- // On the client we don't have access to the remote side identity.
- result = new GenericIdentity(name, protocol);
- return result;
+ if (securityBuffer[1].type != realDataType)
+ {
+ throw new InternalException();
+ }
+
+ newOffset = securityBuffer[1].offset;
+ return securityBuffer[1].size;
}
- internal void CheckThrow(bool authSucessCheck)
+ private static ContextFlagsPal GetContextFlagsPalFromInterop(Interop.SspiCli.ContextFlags win32Flags)
{
- if (_exception != null)
+ ContextFlagsPal flags = ContextFlagsPal.Zero;
+ if ((win32Flags & Interop.SspiCli.ContextFlags.AcceptExtendedError) != 0)
{
- throw _exception;
+ flags |= ContextFlagsPal.AcceptExtendedError;
}
-
- if (authSucessCheck && !IsAuthenticated)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.AcceptIdentify) != 0)
{
- throw new InvalidOperationException(SR.net_auth_noauth);
+ flags |= ContextFlagsPal.AcceptIdentify;
}
- }
-
- //
- // This is to not depend on GC&SafeHandle class if the context is not needed anymore.
- //
- internal void Close()
- {
- // Mark this instance as disposed.
- _exception = new ObjectDisposedException("NegotiateStream");
- if (_context != null)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.AcceptIntegrity) != 0)
{
- _context.CloseContext();
+ flags |= ContextFlagsPal.AcceptIntegrity;
}
- }
-
- internal void ProcessAuthentication(LazyAsyncResult lazyResult)
- {
- CheckThrow(false);
- if (Interlocked.Exchange(ref _nestedAuth, 1) == 1)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.AcceptStream) != 0)
{
- throw new InvalidOperationException(SR.Format(SR.net_io_invalidnestedcall, lazyResult == null ? "BeginAuthenticate" : "Authenticate", "authenticate"));
+ flags |= ContextFlagsPal.AcceptStream;
}
-
- try
+ if ((win32Flags & Interop.SspiCli.ContextFlags.AllocateMemory) != 0)
{
- if (_context.IsServer)
- {
- // Listen for a client blob.
- StartReceiveBlob(lazyResult);
- }
- else
- {
- // Start with the first blob.
- StartSendBlob(null, lazyResult);
- }
+ flags |= ContextFlagsPal.AllocateMemory;
}
- catch (Exception e)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.AllowMissingBindings) != 0)
{
- // Round-trip it through SetException().
- e = SetException(e);
- throw;
+ flags |= ContextFlagsPal.AllowMissingBindings;
}
- finally
+ if ((win32Flags & Interop.SspiCli.ContextFlags.Confidentiality) != 0)
{
- if (lazyResult == null || _exception != null)
- {
- _nestedAuth = 0;
- }
+ flags |= ContextFlagsPal.Confidentiality;
}
- }
-
- internal void EndProcessAuthentication(IAsyncResult result)
- {
- if (result == null)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.Connection) != 0)
{
- throw new ArgumentNullException("asyncResult");
+ flags |= ContextFlagsPal.Connection;
}
-
- LazyAsyncResult lazyResult = result as LazyAsyncResult;
- if (lazyResult == null)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.Delegate) != 0)
{
- throw new ArgumentException(SR.Format(SR.net_io_async_result, result.GetType().FullName), "asyncResult");
+ flags |= ContextFlagsPal.Delegate;
}
-
- if (Interlocked.Exchange(ref _nestedAuth, 0) == 0)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.InitExtendedError) != 0)
{
- throw new InvalidOperationException(SR.Format(SR.net_io_invalidendcall, "EndAuthenticate"));
+ flags |= ContextFlagsPal.InitExtendedError;
}
-
- // No "artificial" timeouts implemented so far, InnerStream controls that.
- lazyResult.InternalWaitForCompletion();
-
- Exception e = lazyResult.Result as Exception;
-
- if (e != null)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.InitIdentify) != 0)
{
- // Round-trip it through the SetException().
- e = SetException(e);
- throw e;
+ flags |= ContextFlagsPal.InitIdentify;
}
- }
-
- private bool CheckSpn()
- {
- if (_context.IsKerberos)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.InitIntegrity) != 0)
{
- return true;
+ flags |= ContextFlagsPal.InitIntegrity;
}
-
- if (_extendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.Never ||
- _extendedProtectionPolicy.CustomServiceNames == null)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.InitManualCredValidation) != 0)
{
- return true;
+ flags |= ContextFlagsPal.InitManualCredValidation;
}
-
- string clientSpn = _context.ClientSpecifiedSpn;
-
- if (String.IsNullOrEmpty(clientSpn))
+ if ((win32Flags & Interop.SspiCli.ContextFlags.InitStream) != 0)
{
- if (_extendedProtectionPolicy.PolicyEnforcement == PolicyEnforcement.WhenSupported)
- {
- return true;
- }
+ flags |= ContextFlagsPal.InitStream;
}
- else
+ if ((win32Flags & Interop.SspiCli.ContextFlags.InitUseSuppliedCreds) != 0)
{
- return _extendedProtectionPolicy.CustomServiceNames.Contains(clientSpn);
+ flags |= ContextFlagsPal.InitUseSuppliedCreds;
}
-
- return false;
- }
-
- //
- // Client side starts here, but server also loops through this method.
- //
- private void StartSendBlob(byte[] message, LazyAsyncResult lazyResult)
- {
- Win32Exception win32exception = null;
- if (message != s_emptyMessage)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.MutualAuth) != 0)
{
- message = GetOutgoingBlob(message, ref win32exception);
+ flags |= ContextFlagsPal.MutualAuth;
}
-
- if (win32exception != null)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.ProxyBindings) != 0)
{
- // Signal remote side on a failed attempt.
- StartSendAuthResetSignal(lazyResult, message, win32exception);
- return;
+ flags |= ContextFlagsPal.ProxyBindings;
}
-
- if (HandshakeComplete)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.ReplayDetect) != 0)
{
- if (_context.IsServer && !CheckSpn())
- {
- Exception exception = new AuthenticationException(SR.net_auth_bad_client_creds_or_target_mismatch);
- int statusCode = ERROR_TRUST_FAILURE;
- message = new byte[8]; //sizeof(long)
-
- for (int i = message.Length - 1; i >= 0; --i)
- {
- message[i] = (byte)(statusCode & 0xFF);
- statusCode = (int)((uint)statusCode >> 8);
- }
-
- StartSendAuthResetSignal(lazyResult, message, exception);
- return;
- }
-
- if (PrivateImpersonationLevel < _expectedImpersonationLevel)
- {
- Exception exception = new AuthenticationException(SR.Format(SR.net_auth_context_expectation, _expectedImpersonationLevel.ToString(), PrivateImpersonationLevel.ToString()));
- int statusCode = ERROR_TRUST_FAILURE;
- message = new byte[8]; //sizeof(long)
-
- for (int i = message.Length - 1; i >= 0; --i)
- {
- message[i] = (byte)(statusCode & 0xFF);
- statusCode = (int)((uint)statusCode >> 8);
- }
-
- StartSendAuthResetSignal(lazyResult, message, exception);
- return;
- }
-
- ProtectionLevel result = _context.IsConfidentialityFlag ? ProtectionLevel.EncryptAndSign : _context.IsIntegrityFlag ? ProtectionLevel.Sign : ProtectionLevel.None;
-
- if (result < _expectedProtectionLevel)
- {
- Exception exception = new AuthenticationException(SR.Format(SR.net_auth_context_expectation, result.ToString(), _expectedProtectionLevel.ToString()));
- int statusCode = ERROR_TRUST_FAILURE;
- message = new byte[8]; //sizeof(long)
-
- for (int i = message.Length - 1; i >= 0; --i)
- {
- message[i] = (byte)(statusCode & 0xFF);
- statusCode = (int)((uint)statusCode >> 8);
- }
-
- StartSendAuthResetSignal(lazyResult, message, exception);
- return;
- }
-
- // Signal remote party that we are done
- _framer.WriteHeader.MessageId = FrameHeader.HandshakeDoneId;
- if (_context.IsServer)
- {
- // Server may complete now because client SSPI would not complain at this point.
- _remoteOk = true;
-
- // However the client will wait for server to send this ACK
- //Force signaling server OK to the client
- if (message == null)
- {
- message = s_emptyMessage;
- }
- }
+ flags |= ContextFlagsPal.ReplayDetect;
}
- else if (message == null || message == s_emptyMessage)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.SequenceDetect) != 0)
{
- throw new InternalException();
+ flags |= ContextFlagsPal.SequenceDetect;
}
-
- if (message != null)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.UnverifiedTargetName) != 0)
{
- //even if we are completed, there could be a blob for sending.
- if (lazyResult == null)
- {
- _framer.WriteMessage(message);
- }
- else
- {
- IAsyncResult ar = _framer.BeginWriteMessage(message, s_writeCallback, lazyResult);
- if (!ar.CompletedSynchronously)
- {
- return;
- }
- _framer.EndWriteMessage(ar);
- }
+ flags |= ContextFlagsPal.UnverifiedTargetName;
}
- CheckCompletionBeforeNextReceive(lazyResult);
- }
-
- //
- // This will check and logically complete the auth handshake.
- //
- private void CheckCompletionBeforeNextReceive(LazyAsyncResult lazyResult)
- {
- if (HandshakeComplete && _remoteOk)
+ if ((win32Flags & Interop.SspiCli.ContextFlags.UseSessionKey) != 0)
{
- // We are done with success.
- if (lazyResult != null)
- {
- lazyResult.InvokeCallback();
- }
-
- return;
+ flags |= ContextFlagsPal.UseSessionKey;
}
-
- StartReceiveBlob(lazyResult);
+ return flags;
}
- //
- // Server side starts here, but client also loops through this method.
- //
- private void StartReceiveBlob(LazyAsyncResult lazyResult)
+ private static Interop.SspiCli.ContextFlags GetInteropFromContextFlagsPal(ContextFlagsPal flags)
{
- byte[] message;
- if (lazyResult == null)
+ Interop.SspiCli.ContextFlags win32Flags = Interop.SspiCli.ContextFlags.Zero;
+ if ((flags & ContextFlagsPal.AcceptExtendedError) != 0)
{
- message = _framer.ReadMessage();
+ win32Flags |= Interop.SspiCli.ContextFlags.AcceptExtendedError;
}
- else
+ if ((flags & ContextFlagsPal.AcceptIdentify) != 0)
{
- IAsyncResult ar = _framer.BeginReadMessage(s_readCallback, lazyResult);
- if (!ar.CompletedSynchronously)
- {
- return;
- }
-
- message = _framer.EndReadMessage(ar);
+ win32Flags |= Interop.SspiCli.ContextFlags.AcceptIdentify;
}
-
- ProcessReceivedBlob(message, lazyResult);
- }
-
- private void ProcessReceivedBlob(byte[] message, LazyAsyncResult lazyResult)
- {
- // This is an EOF otherwise we would get at least *empty* message but not a null one.
- if (message == null)
+ if ((flags & ContextFlagsPal.AcceptIntegrity) != 0)
{
- throw new AuthenticationException(SR.net_auth_eof, null);
+ win32Flags |= Interop.SspiCli.ContextFlags.AcceptIntegrity;
}
-
- // Process Header information.
- if (_framer.ReadHeader.MessageId == FrameHeader.HandshakeErrId)
+ if ((flags & ContextFlagsPal.AcceptStream) != 0)
{
- Win32Exception e = null;
- if (message.Length >= 8) // sizeof(long)
- {
- // Try to recover remote win32 Exception.
- long error = 0;
- for (int i = 0; i < 8; ++i)
- {
- error = (error << 8) + message[i];
- }
-
- e = new Win32Exception((int)error);
- }
-
- if (e != null)
- {
- if (e.NativeErrorCode == (int)Interop.SecurityStatus.LogonDenied)
- {
- throw new InvalidCredentialException(SR.net_auth_bad_client_creds, e);
- }
-
- if (e.NativeErrorCode == ERROR_TRUST_FAILURE)
- {
- throw new AuthenticationException(SR.net_auth_context_expectation_remote, e);
- }
- }
-
- throw new AuthenticationException(SR.net_auth_alert, e);
+ win32Flags |= Interop.SspiCli.ContextFlags.AcceptStream;
}
-
- if (_framer.ReadHeader.MessageId == FrameHeader.HandshakeDoneId)
+ if ((flags & ContextFlagsPal.AllocateMemory) != 0)
{
- _remoteOk = true;
+ win32Flags |= Interop.SspiCli.ContextFlags.AllocateMemory;
}
- else if (_framer.ReadHeader.MessageId != FrameHeader.HandshakeId)
+ if ((flags & ContextFlagsPal.AllowMissingBindings) != 0)
{
- throw new AuthenticationException(SR.Format(SR.net_io_header_id, "MessageId", _framer.ReadHeader.MessageId, FrameHeader.HandshakeId), null);
+ win32Flags |= Interop.SspiCli.ContextFlags.AllowMissingBindings;
}
-
- CheckCompletionBeforeNextSend(message, lazyResult);
- }
-
- //
- // This will check and logically complete the auth handshake.
- //
- private void CheckCompletionBeforeNextSend(byte[] message, LazyAsyncResult lazyResult)
- {
- //If we are done don't go into send.
- if (HandshakeComplete)
+ if ((flags & ContextFlagsPal.Confidentiality) != 0)
{
- if (!_remoteOk)
- {
- throw new AuthenticationException(SR.Format(SR.net_io_header_id, "MessageId", _framer.ReadHeader.MessageId, FrameHeader.HandshakeDoneId), null);
- }
- if (lazyResult != null)
- {
- lazyResult.InvokeCallback();
- }
-
- return;
+ win32Flags |= Interop.SspiCli.ContextFlags.Confidentiality;
}
-
- // Not yet done, get a new blob and send it if any.
- StartSendBlob(message, lazyResult);
- }
-
- //
- // This is to reset auth state on the remote side.
- // If this write succeeds we will allow auth retrying.
- //
- private void StartSendAuthResetSignal(LazyAsyncResult lazyResult, byte[] message, Exception exception)
- {
- _framer.WriteHeader.MessageId = FrameHeader.HandshakeErrId;
-
- Win32Exception win32exception = exception as Win32Exception;
-
- if (win32exception != null && win32exception.NativeErrorCode == (int)Interop.SecurityStatus.LogonDenied)
+ if ((flags & ContextFlagsPal.Connection) != 0)
{
- if (IsServer)
- {
- exception = new InvalidCredentialException(SR.net_auth_bad_client_creds, exception);
- }
- else
- {
- exception = new InvalidCredentialException(SR.net_auth_bad_client_creds_or_target_mismatch, exception);
- }
+ win32Flags |= Interop.SspiCli.ContextFlags.Connection;
}
-
- if (!(exception is AuthenticationException))
+ if ((flags & ContextFlagsPal.Delegate) != 0)
{
- exception = new AuthenticationException(SR.net_auth_SSPI, exception);
+ win32Flags |= Interop.SspiCli.ContextFlags.Delegate;
}
-
- if (lazyResult == null)
+ if ((flags & ContextFlagsPal.InitExtendedError) != 0)
{
- _framer.WriteMessage(message);
+ win32Flags |= Interop.SspiCli.ContextFlags.InitExtendedError;
}
- else
+ if ((flags & ContextFlagsPal.InitIdentify) != 0)
{
- lazyResult.Result = exception;
- IAsyncResult ar = _framer.BeginWriteMessage(message, s_writeCallback, lazyResult);
- if (!ar.CompletedSynchronously)
- {
- return;
- }
-
- _framer.EndWriteMessage(ar);
+ win32Flags |= Interop.SspiCli.ContextFlags.InitIdentify;
}
-
- _canRetryAuthentication = true;
- throw exception;
- }
-
- private static void WriteCallback(IAsyncResult transportResult)
- {
- if ((transportResult.AsyncState is LazyAsyncResult))
+ if ((flags & ContextFlagsPal.InitIntegrity) != 0)
{
- if (GlobalLog.IsEnabled)
- {
- GlobalLog.Assert("WriteCallback|State type is wrong, expected LazyAsyncResult.");
- }
- Debug.Fail("WriteCallback|State type is wrong, expected LazyAsyncResult.");
+ win32Flags |= Interop.SspiCli.ContextFlags.InitIntegrity;
}
-
- if (transportResult.CompletedSynchronously)
+ if ((flags & ContextFlagsPal.InitManualCredValidation) != 0)
{
- return;
+ win32Flags |= Interop.SspiCli.ContextFlags.InitManualCredValidation;
}
-
- LazyAsyncResult lazyResult = (LazyAsyncResult)transportResult.AsyncState;
-
- // Async completion.
- try
+ if ((flags & ContextFlagsPal.InitStream) != 0)
{
- NegoState authState = (NegoState)lazyResult.AsyncObject;
- authState._framer.EndWriteMessage(transportResult);
-
- // Special case for an error notification.
- if (lazyResult.Result is Exception)
- {
- authState._canRetryAuthentication = true;
- throw (Exception)lazyResult.Result;
- }
-
- authState.CheckCompletionBeforeNextReceive(lazyResult);
+ win32Flags |= Interop.SspiCli.ContextFlags.InitStream;
}
- catch (Exception e)
+ if ((flags & ContextFlagsPal.InitUseSuppliedCreds) != 0)
{
- if (lazyResult.InternalPeekCompleted)
- {
- // This will throw on a worker thread.
- throw;
- }
-
- lazyResult.InvokeCallback(e);
+ win32Flags |= Interop.SspiCli.ContextFlags.InitUseSuppliedCreds;
}
- }
-
- private static void ReadCallback(IAsyncResult transportResult)
- {
- if ((transportResult.AsyncState is LazyAsyncResult))
+ if ((flags & ContextFlagsPal.MutualAuth) != 0)
{
- if (GlobalLog.IsEnabled)
- {
- GlobalLog.Assert("ReadCallback|State type is wrong, expected LazyAsyncResult.");
- }
- Debug.Fail("ReadCallback|State type is wrong, expected LazyAsyncResult.");
+ win32Flags |= Interop.SspiCli.ContextFlags.MutualAuth;
}
-
- if (transportResult.CompletedSynchronously)
+ if ((flags & ContextFlagsPal.ProxyBindings) != 0)
{
- return;
+ win32Flags |= Interop.SspiCli.ContextFlags.ProxyBindings;
}
-
- LazyAsyncResult lazyResult = (LazyAsyncResult)transportResult.AsyncState;
-
- // Async completion.
- try
+ if ((flags & ContextFlagsPal.ReplayDetect) != 0)
{
- NegoState authState = (NegoState)lazyResult.AsyncObject;
- byte[] message = authState._framer.EndReadMessage(transportResult);
- authState.ProcessReceivedBlob(message, lazyResult);
+ win32Flags |= Interop.SspiCli.ContextFlags.ReplayDetect;
}
- catch (Exception e)
+ if ((flags & ContextFlagsPal.SequenceDetect) != 0)
{
- if (lazyResult.InternalPeekCompleted)
- {
- // This will throw on a worker thread.
- throw;
- }
-
- lazyResult.InvokeCallback(e);
+ win32Flags |= Interop.SspiCli.ContextFlags.SequenceDetect;
}
- }
-
- private unsafe byte[] GetOutgoingBlob(byte[] incomingBlob, ref Win32Exception e)
- {
- Interop.SecurityStatus statusCode;
- byte[] message = _context.GetOutgoingBlob(incomingBlob, false, out statusCode);
-
- if (((int)statusCode & unchecked((int)0x80000000)) != 0)
+ if ((flags & ContextFlagsPal.UnverifiedTargetName) != 0)
{
- e = new System.ComponentModel.Win32Exception((int)statusCode);
-
- message = new byte[8]; //sizeof(long)
- for (int i = message.Length - 1; i >= 0; --i)
- {
- message[i] = (byte)((uint)statusCode & 0xFF);
- statusCode = (Interop.SecurityStatus)((uint)statusCode >> 8);
- }
+ win32Flags |= Interop.SspiCli.ContextFlags.UnverifiedTargetName;
}
-
- if (message != null && message.Length == 0)
+ if ((flags & ContextFlagsPal.UseSessionKey) != 0)
{
- message = s_emptyMessage;
+ win32Flags |= Interop.SspiCli.ContextFlags.UseSessionKey;
}
-
- return message;
- }
-
- internal int EncryptData(byte[] buffer, int offset, int count, ref byte[] outBuffer)
- {
- CheckThrow(true);
-
- // SSPI seems to ignore this sequence number.
- ++_writeSequenceNumber;
- return _context.Encrypt(buffer, offset, count, ref outBuffer, _writeSequenceNumber);
- }
-
- internal int DecryptData(byte[] buffer, int offset, int count, out int newOffset)
- {
- CheckThrow(true);
-
- // SSPI seems to ignore this sequence number.
- ++_readSequenceNumber;
- return _context.Decrypt(buffer, offset, count, out newOffset, _readSequenceNumber);
+ return win32Flags;
}
}
}
diff --git a/src/System.Net.Security/src/System/Net/SslStreamPal.Unix.cs b/src/System.Net.Security/src/System/Net/SslStreamPal.Unix.cs
index 755c0fbae642..af722050ad2f 100644
--- a/src/System.Net.Security/src/System/Net/SslStreamPal.Unix.cs
+++ b/src/System.Net.Security/src/System/Net/SslStreamPal.Unix.cs
@@ -44,7 +44,7 @@ public static SecurityStatusPal InitializeSecurityContext(SafeFreeCredentials cr
public static SafeFreeCredentials AcquireCredentialsHandle(X509Certificate certificate,
SslProtocols protocols, EncryptionPolicy policy, bool isServer)
{
- return new SafeFreeCredentials(certificate, protocols, policy);
+ return new SafeFreeSslCredentials(certificate, protocols, policy);
}
public static SecurityStatusPal EncryptMessage(SafeDeleteContext securityContext, byte[] buffer, int size, int headerSize, int trailerSize, out int resultSize)
@@ -66,7 +66,7 @@ public static SecurityStatusPal DecryptMessage(SafeDeleteContext securityContext
public static SafeFreeContextBufferChannelBinding QueryContextChannelBinding(SafeDeleteContext securityContext, ChannelBindingKind attribute)
{
- SafeChannelBindingHandle bindingHandle = Interop.OpenSsl.QueryChannelBinding(securityContext.SslContext, attribute);
+ SafeChannelBindingHandle bindingHandle = Interop.OpenSsl.QueryChannelBinding(((SafeDeleteSslContext)securityContext).SslContext, attribute);
var refHandle = bindingHandle == null ? null : new SafeFreeContextBufferChannelBinding(bindingHandle);
return refHandle;
}
@@ -78,7 +78,7 @@ public static void QueryContextStreamSizes(SafeDeleteContext securityContext, ou
public static void QueryContextConnectionInfo(SafeDeleteContext securityContext, out SslConnectionInfo connectionInfo)
{
- connectionInfo = new SslConnectionInfo(securityContext.SslContext);
+ connectionInfo = new SslConnectionInfo(((SafeDeleteSslContext)securityContext).SslContext);
}
private static SecurityStatusPal HandshakeInternal(SafeFreeCredentials credential, ref SafeDeleteContext context,
@@ -90,7 +90,7 @@ private static SecurityStatusPal HandshakeInternal(SafeFreeCredentials credentia
{
if ((null == context) || context.IsInvalid)
{
- context = new SafeDeleteContext(credential, isServer, remoteCertRequired);
+ context = new SafeDeleteSslContext(credential as SafeFreeSslCredentials, isServer, remoteCertRequired);
}
byte[] output = null;
@@ -99,11 +99,11 @@ private static SecurityStatusPal HandshakeInternal(SafeFreeCredentials credentia
if (null == inputBuffer)
{
- done = Interop.OpenSsl.DoSslHandshake(context.SslContext, null, 0, 0, out output, out outputSize);
+ done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, null, 0, 0, out output, out outputSize);
}
else
{
- done = Interop.OpenSsl.DoSslHandshake(context.SslContext, inputBuffer.token, inputBuffer.offset, inputBuffer.size, out output, out outputSize);
+ done = Interop.OpenSsl.DoSslHandshake(((SafeDeleteSslContext)context).SslContext, inputBuffer.token, inputBuffer.offset, inputBuffer.size, out output, out outputSize);
}
outputBuffer.size = outputSize;
@@ -116,7 +116,7 @@ private static SecurityStatusPal HandshakeInternal(SafeFreeCredentials credentia
{
// TODO: This Debug.Fail is triggering on Linux in many test cases #4317
// Debug.Fail("Exception Caught. - " + ex);
- return SecurityStatusPal.InternalError;
+ return SecurityStatusPal.InternalError;
}
}
@@ -128,7 +128,7 @@ private static SecurityStatusPal EncryptDecryptHelper(SafeDeleteContext security
Interop.Ssl.SslErrorCode errorCode = Interop.Ssl.SslErrorCode.SSL_ERROR_NONE;
- SafeSslHandle scHandle = securityContext.SslContext;
+ SafeSslHandle scHandle = ((SafeDeleteSslContext)securityContext).SslContext;
resultSize = encrypt ?
Interop.OpenSsl.Encrypt(scHandle, buffer, offset, size, out errorCode) :
diff --git a/src/System.Net.Security/src/System/Net/SslStreamPal.Windows.cs b/src/System.Net.Security/src/System/Net/SslStreamPal.Windows.cs
index 5848c17c84a5..26ebc7e50e05 100644
--- a/src/System.Net.Security/src/System/Net/SslStreamPal.Windows.cs
+++ b/src/System.Net.Security/src/System/Net/SslStreamPal.Windows.cs
@@ -298,12 +298,12 @@ private static SafeFreeCredentials AcquireCredentialsHandle(Interop.SspiCli.Cred
}
}
- private static SecurityStatusPal GetSecurityStatusPalFromWin32Int(int win32SecurityStatus)
+ internal static SecurityStatusPal GetSecurityStatusPalFromWin32Int(int win32SecurityStatus)
{
return GetSecurityStatusPalFromInterop((Interop.SecurityStatus)win32SecurityStatus);
}
- private static SecurityStatusPal GetSecurityStatusPalFromInterop(Interop.SecurityStatus win32SecurityStatus)
+ internal static SecurityStatusPal GetSecurityStatusPalFromInterop(Interop.SecurityStatus win32SecurityStatus)
{
switch (win32SecurityStatus)
{
@@ -391,7 +391,7 @@ private static SecurityStatusPal GetSecurityStatusPalFromInterop(Interop.Securit
}
}
- private static Interop.SecurityStatus GetInteropFromSecurityStatusPal(SecurityStatusPal status)
+ internal static Interop.SecurityStatus GetInteropFromSecurityStatusPal(SecurityStatusPal status)
{
switch (status)
{