diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/SecuritySafeHandles.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/SecuritySafeHandles.cs
new file mode 100644
index 000000000000..e12b127d70c3
--- /dev/null
+++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/SecuritySafeHandles.cs
@@ -0,0 +1,98 @@
+// 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 Microsoft.Win32.SafeHandles;
+using System.Diagnostics;
+
+namespace System.Net.Security
+{
+ internal sealed partial class SafeFreeNegoCredentials : SafeFreeCredentials
+ {
+ private readonly string _password;
+
+ public string Password
+ {
+ get { return _password; }
+ }
+
+ public SafeFreeNegoCredentials(bool ntlmOnly, string username, string password, string domain) : base(IntPtr.Zero, true)
+ {
+ _isNtlm = ntlmOnly;
+ _isdefault = string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password);
+ if (!ntlmOnly)
+ {
+ try
+ {
+ _credential = SafeGssCredHandle.Create(username, password, domain);
+ }
+ catch
+ {
+ // NTLM fallback is not possible with default credentials
+ if (_isdefault)
+ {
+ throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred);
+ }
+
+ _isNtlm = true;
+ }
+ }
+
+ // Even if Kerberos TGT could be obtained, we might later need
+ // to fall back to NTLM if service ticket cannot be fetched
+ _username = username;
+ _password = password;
+ _domain = domain;
+ }
+ }
+
+ internal sealed partial class SafeDeleteNegoContext : SafeDeleteContext
+ {
+ private readonly Interop.NetNtlmNative.NtlmFlags _flags;
+ private Interop.HeimdalNtlm.SigningKey _serverSignKey;
+ private Interop.HeimdalNtlm.SealingKey _serverSealKey;
+ private Interop.HeimdalNtlm.SigningKey _clientSignKey;
+ private Interop.HeimdalNtlm.SealingKey _clientSealKey;
+
+ public Interop.NetNtlmNative.NtlmFlags Flags
+ {
+ get { return _flags; }
+ }
+
+ public SafeDeleteNegoContext(SafeFreeNegoCredentials credential, Interop.NetNtlmNative.NtlmFlags flags)
+ : base(credential)
+ {
+ _flags = flags;
+ _isNtlm = true;
+ }
+
+ public void SetKeys(byte[] sessionKey)
+ {
+ Interop.HeimdalNtlm.CreateKeys(sessionKey, out _serverSignKey, out _serverSealKey, out _clientSignKey, out _clientSealKey);
+ }
+
+ public byte[] MakeClientSignature(byte[] buffer, int offset, int count)
+ {
+ Debug.Assert(_clientSignKey != null, "_clientSignKey cannot be null");
+ return _clientSignKey.Sign(_clientSealKey, buffer, offset, count);
+ }
+
+ public byte[] MakeServerSignature(byte[] buffer, int offset, int count)
+ {
+ Debug.Assert(_serverSignKey != null, "_serverSignKey cannot be null");
+ return _serverSignKey.Sign(_serverSealKey, buffer, offset, count);
+ }
+
+ public byte[] Encrypt(byte[] buffer, int offset, int count)
+ {
+ Debug.Assert(_clientSignKey != null, "_clientSealKey cannot be null");
+ return _clientSealKey.SealOrUnseal(buffer, offset, count);
+ }
+
+ public byte[] Decrypt(byte[] buffer, int offset, int count)
+ {
+ Debug.Assert(_serverSignKey != null, "_serverSealKey cannot be null");
+ return _serverSealKey.SealOrUnseal(buffer, offset, count);
+ }
+ }
+}
diff --git a/src/Common/src/Interop/OSX/System.Net.Security.Native/SecuritySafeHandles.cs b/src/Common/src/Interop/OSX/System.Net.Security.Native/SecuritySafeHandles.cs
new file mode 100644
index 000000000000..8509141d447f
--- /dev/null
+++ b/src/Common/src/Interop/OSX/System.Net.Security.Native/SecuritySafeHandles.cs
@@ -0,0 +1,79 @@
+// 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.Runtime.InteropServices;
+using Microsoft.Win32.SafeHandles;
+
+namespace System.Net.Security
+{
+ internal sealed partial class SafeFreeNegoCredentials : SafeFreeCredentials
+ {
+
+ public SafeFreeNegoCredentials(bool ntlmOnly, string username, string password, string domain) : base(IntPtr.Zero, true)
+ {
+ _isNtlm = ntlmOnly;
+ _isdefault = string.IsNullOrWhiteSpace(username) || string.IsNullOrWhiteSpace(password);
+ _username = username;
+ _domain = domain;
+ _credential = SafeGssCredHandle.Create(username, password, domain);
+ }
+ }
+
+ internal sealed partial class SafeDeleteNegoContext : SafeDeleteContext
+ {
+ private const char At = '@';
+ public SafeDeleteNegoContext(SafeFreeNegoCredentials credential)
+ : base(credential)
+ {
+ // Try to construct target in user@domain format
+ string targetName = credential.UserName;
+ string domain = credential.Domain;
+
+ //remove any leading and trailing whitespace
+ if (domain != null)
+ {
+ domain = domain.Trim();
+ }
+
+ if ((targetName.IndexOf(At) < 0) && !string.IsNullOrEmpty(domain))
+ {
+ targetName += At + domain;
+ }
+
+ try
+ {
+ _targetName = SafeGssNameHandle.CreatePrincipal(targetName);
+ }
+ catch
+ {
+ Dispose();
+ throw;
+ }
+ }
+
+ public byte[] MakeClientSignature(byte[] buffer, int offset, int count)
+ {
+ //MakeClientSignature is not supported on OSX
+ throw new PlatformNotSupportedException();
+ }
+
+ public byte[] MakeServerSignature(byte[] buffer, int offset, int count)
+ {
+ //MakeServerSignature is not supported on OSX
+ throw new PlatformNotSupportedException();
+ }
+
+ public byte[] Encrypt(byte[] buffer, int offset, int count)
+ {
+ //Encrypt is not supported on OSX
+ throw new PlatformNotSupportedException();
+ }
+
+ public byte[] Decrypt(byte[] buffer, int offset, int count)
+ {
+ //Decrypt is not supported on OSX
+ throw new PlatformNotSupportedException();
+ }
+ }
+}
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
index e4e07954e02c..0237bb657fb4 100644
--- a/src/Common/src/Interop/Unix/System.Net.Security.Native/SecuritySafeHandles.cs
+++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/SecuritySafeHandles.cs
@@ -10,9 +10,33 @@
namespace System.Net.Security
{
- internal sealed class SafeFreeNegoCredentials : SafeFreeCredentials
+ internal sealed partial class SafeFreeNegoCredentials : SafeFreeCredentials
{
private SafeGssCredHandle _credential;
+ private bool _isdefault;
+ private readonly string _username;
+ private readonly string _domain;
+ private bool _isNtlm;
+
+ public bool IsDefault
+ {
+ get { return _isdefault; }
+ }
+
+ public string UserName
+ {
+ get { return _username; }
+ }
+
+ public string Domain
+ {
+ get { return _domain; }
+ }
+
+ public bool IsNtlm
+ {
+ get { return _isNtlm; }
+ }
public SafeGssCredHandle GssCredential
{
@@ -39,10 +63,11 @@ protected override bool ReleaseHandle()
}
}
- internal sealed class SafeDeleteNegoContext : SafeDeleteContext
+ internal sealed partial class SafeDeleteNegoContext : SafeDeleteContext
{
private SafeGssNameHandle _targetName;
private SafeGssContextHandle _context;
+ private bool _isNtlm;
public SafeGssNameHandle TargetName
{
@@ -54,6 +79,11 @@ public SafeGssContextHandle GssContext
get { return _context; }
}
+ public bool IsNtlm
+ {
+ get { return _isNtlm; }
+ }
+
public SafeDeleteNegoContext(SafeFreeNegoCredentials credential, string targetName)
: base(credential)
{
@@ -69,10 +99,11 @@ public SafeDeleteNegoContext(SafeFreeNegoCredentials credential, string targetNa
}
}
- public void SetGssContext(SafeGssContextHandle context)
+ public void SetGssContext(SafeGssContextHandle context, bool isNtlm)
{
Debug.Assert(!context.IsInvalid, "Invalid context passed to SafeDeleteNegoContext");
_context = context;
+ _isNtlm = isNtlm;
}
protected override void Dispose(bool disposing)
diff --git a/src/System.Net.Security/src/Resources/Strings.resx b/src/System.Net.Security/src/Resources/Strings.resx
index 3634df93afa9..1fb467cb5991 100644
--- a/src/System.Net.Security/src/Resources/Strings.resx
+++ b/src/System.Net.Security/src/Resources/Strings.resx
@@ -432,13 +432,16 @@
Insufficient buffer space. Required: {0} Actual: {1}.
+
+ NTLM authentication is not possible with default credentials which are no-op.
+
No support for channel binding on operating systems other than Windows
-
- NTLM is not supported
-
Server implementation is not supported
+
+ NTLM operation failed with status: {0})
+
diff --git a/src/System.Net.Security/src/System.Net.Security.csproj b/src/System.Net.Security/src/System.Net.Security.csproj
index 28e37dc86c3e..8cb005c4733a 100644
--- a/src/System.Net.Security/src/System.Net.Security.csproj
+++ b/src/System.Net.Security/src/System.Net.Security.csproj
@@ -46,8 +46,6 @@
-
-
@@ -151,8 +149,6 @@
-
-
@@ -238,8 +234,6 @@
-
-
@@ -348,6 +342,60 @@
+
+
+
+
+
+ Common\Interop\Linux\System.Net.Security.Native\SecuritySafeHandles.cs
+
+
+ Common\Interop\Linux\System.Net.Ntlm.Native\Interop.NetNtlmNative.cs
+
+
+ Common\Interop\Linux\System.Net.Ntlm.Native\Interop.NtlmException.cs
+
+
+ Common\Interop\Linux\System.Net.Ntlm.Native\Interop.Ntlm.cs
+
+
+ Common\Interop\Linux\System.Net.Ntlm.Native\Interop.NtlmBuffer.cs
+
+
+ Common\Interop\Linux\System.Net.Ntlm.Native\Interop.NtlmType3Message.cs
+
+
+ Common\Microsoft\Win32\SafeHandles\NtlmSecuritySafeHandles.cs
+
+
+ Common\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs
+
+
+ Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs
+
+
+ Microsoft\Win32\SafeHandles\SafeEvpMdCtxHandle.Unix.cs
+
+
+ Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Cipher.cs
+
+
+ Common\Microsoft\Win32\SafeHandles\SafeEvpCipherCtxHandle.Unix.cs
+
+
+ Common\Microsoft\Win32\SafeHandles\SafeHmacCtxHandle.Unix.cs
+
+
+
+
+
+
+
+ Common\Interop\OSX\System.Net.Security.Native\SecuritySafeHandles.cs
+
+
+
+
diff --git a/src/System.Net.Security/src/System/Net/NegotiateStreamPal.Linux.cs b/src/System.Net.Security/src/System/Net/NegotiateStreamPal.Linux.cs
new file mode 100644
index 000000000000..e88f68229f8e
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/NegotiateStreamPal.Linux.cs
@@ -0,0 +1,91 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Win32.SafeHandles;
+
+namespace System.Net.Security
+{
+ internal static partial class NegotiateStreamPal
+ {
+ private static bool EstablishNtlmSecurityContext(
+ SafeFreeNegoCredentials credential,
+ ref SafeDeleteContext context,
+ string targetName,
+ ContextFlagsPal inFlags,
+ SecurityBuffer inputBuffer,
+ SecurityBuffer outputBuffer,
+ ref ContextFlagsPal outFlags)
+ {
+ bool retVal;
+ Interop.NetNtlmNative.NtlmFlags flags;
+ if (null == context)
+ {
+ flags = GetInteropNtlmFromContextFlagsPal(inFlags);
+ context = new SafeDeleteNegoContext(credential, flags);
+ outputBuffer.token = Interop.Ntlm.CreateNegotiateMessage((uint) flags);
+ retVal = false;
+ }
+ else
+ {
+ SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext) context;
+ flags = negoContext.Flags;
+ byte[] sessionKey;
+ outputBuffer.token = Interop.Ntlm.CreateAuthenticateMessage(
+ (uint) flags,
+ credential.UserName,
+ credential.Password,
+ credential.Domain,
+ inputBuffer.token,
+ inputBuffer.offset,
+ inputBuffer.size,
+ out sessionKey);
+ negoContext.SetKeys(sessionKey);
+ retVal = true;
+ }
+
+ outFlags = inFlags;
+ outputBuffer.size = outputBuffer.token.Length;
+ return retVal;
+ }
+
+ private static ContextFlagsPal GetContextFlagsPalFromInteropNtlm(Interop.NetNtlmNative.NtlmFlags ntlmFlags)
+ {
+ ContextFlagsPal flags = ContextFlagsPal.Zero;
+ if ((ntlmFlags & Interop.NetNtlmNative.NtlmFlags.NTLMSSP_NEGOTIATE_SEAL) != 0)
+ {
+ flags |= ContextFlagsPal.Confidentiality;
+ }
+
+ if ((ntlmFlags & Interop.NetNtlmNative.NtlmFlags.NTLMSSP_NEGOTIATE_SIGN) != 0)
+ {
+ // No NTLM server support so not setting AcceptIntegrity flag
+ flags |= ContextFlagsPal.InitIntegrity;
+ flags |= ContextFlagsPal.ReplayDetect | ContextFlagsPal.SequenceDetect;
+ }
+
+ return flags;
+ }
+
+ private static Interop.NetNtlmNative.NtlmFlags GetInteropNtlmFromContextFlagsPal(ContextFlagsPal flags)
+ {
+ Interop.NetNtlmNative.NtlmFlags ntlmFlags = Interop.NetNtlmNative.NtlmFlags.NTLMSSP_NEGOTIATE_UNICODE | Interop.NetNtlmNative.NtlmFlags.NTLMSSP_REQUEST_TARGET;
+ if ((flags & (ContextFlagsPal.AcceptIntegrity | ContextFlagsPal.InitIntegrity | ContextFlagsPal.Confidentiality)) != 0)
+ {
+ ntlmFlags |= Interop.NetNtlmNative.NtlmFlags.NTLMSSP_NEGOTIATE_SIGN
+ | Interop.NetNtlmNative.NtlmFlags.NTLMSSP_NEGOTIATE_ALWAYS_SIGN
+ | Interop.NetNtlmNative.NtlmFlags.NTLMSSP_NEGOTIATE_NTLM
+ | Interop.NetNtlmNative.NtlmFlags.NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY
+ | Interop.NetNtlmNative.NtlmFlags.NTLMSSP_NEGOTIATE_128
+ | Interop.NetNtlmNative.NtlmFlags.NTLMSSP_NEGOTIATE_KEY_EXCH;
+ }
+
+ if ((flags & ContextFlagsPal.Confidentiality) != 0)
+ {
+ ntlmFlags |= Interop.NetNtlmNative.NtlmFlags.NTLMSSP_NEGOTIATE_SEAL;
+ }
+
+ return ntlmFlags;
+ }
+ }
+}
diff --git a/src/System.Net.Security/src/System/Net/NegotiateStreamPal.OSX.cs b/src/System.Net.Security/src/System/Net/NegotiateStreamPal.OSX.cs
new file mode 100644
index 000000000000..ce6f1ffab95b
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/NegotiateStreamPal.OSX.cs
@@ -0,0 +1,51 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+using Microsoft.Win32.SafeHandles;
+
+namespace System.Net.Security
+{
+ internal static partial class NegotiateStreamPal
+ {
+ private static bool EstablishNtlmSecurityContext(
+ SafeFreeNegoCredentials credential,
+ ref SafeDeleteContext context,
+ string targetName,
+ ContextFlagsPal inFlags,
+ SecurityBuffer inputBuffer,
+ SecurityBuffer outputBuffer,
+ ref ContextFlagsPal outFlags)
+ {
+ if (context == null)
+ {
+ context = new SafeDeleteNegoContext(credential);
+ }
+
+ SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext) context;
+ SafeGssContextHandle contextHandle = negoContext.GssContext;
+
+ uint outputFlags;
+ Interop.NetSecurityNative.GssFlags inputFlags = ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags);
+ bool done = Interop.GssApi.EstablishSecurityContext(
+ ref contextHandle,
+ credential.GssCredential,
+ true,
+ negoContext.TargetName,
+ inputFlags,
+ ((inputBuffer != null) ? inputBuffer.token : null),
+ out outputBuffer.token,
+ out outputFlags);
+
+ outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop((Interop.NetSecurityNative.GssFlags) outputFlags);
+
+ // Save the inner context handle for further calls to NetSecurityNative
+ if (null == negoContext.GssContext)
+ {
+ negoContext.SetGssContext(contextHandle, true);
+ }
+
+ return done;
+ }
+ }
+}
diff --git a/src/System.Net.Security/src/System/Net/NegotiateStreamPal.Unix.cs b/src/System.Net.Security/src/System/Net/NegotiateStreamPal.Unix.cs
index c028c9e4d0dc..49bf17c99d7e 100644
--- a/src/System.Net.Security/src/System/Net/NegotiateStreamPal.Unix.cs
+++ b/src/System.Net.Security/src/System/Net/NegotiateStreamPal.Unix.cs
@@ -22,12 +22,15 @@ namespace System.Net.Security
//
// This is part of the NegotiateStream PAL.
//
- internal static class NegotiateStreamPal
+ internal static partial class NegotiateStreamPal
{
// value should match the Windows sspicli NTE_FAIL value
// defined in winerror.h
private const int NTE_FAIL = unchecked((int)0x80090020);
+ // In case of NTLM input bytes are preceded by a signature of 16 bytes
+ private const int NtlmSignatureLength = 16;
+
internal static IIdentity GetIdentity(NTAuthentication context)
{
Debug.Assert(!context.IsServer, "GetIdentity: Server is not supported");
@@ -46,7 +49,8 @@ internal static string QueryContextAssociatedName(SafeDeleteContext securityCont
internal static string QueryContextAuthenticationPackage(SafeDeleteContext securityContext)
{
- return NegotiationInfoClass.Kerberos;
+ SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
+ return negoContext.IsNtlm ? NegotiationInfoClass.NTLM : NegotiationInfoClass.Kerberos;
}
internal static int QueryMaxTokenSize(string package)
@@ -73,25 +77,26 @@ internal static SafeFreeCredentials AcquireCredentialsHandle(string package, boo
}
SafeFreeCredentials outCredential;
- bool isNtlm = string.Equals(package, NegotiationInfoClass.NTLM);
-
- if (isNtlm)
+ bool ntlmOnly = string.Equals(package, NegotiationInfoClass.NTLM, StringComparison.OrdinalIgnoreCase);
+ bool isEmptyCredential = string.IsNullOrWhiteSpace(credential.UserName) || string.IsNullOrWhiteSpace(credential.Password);
+ if (ntlmOnly && isEmptyCredential)
{
- throw new PlatformNotSupportedException(SR.net_nego_ntlm_not_supported);
+ // NTLM authentication is not possible with default credentials which are no-op
+ throw new PlatformNotSupportedException(SR.net_ntlm_not_possible_default_cred);
}
if (string.IsNullOrEmpty(credential.UserName) || string.IsNullOrEmpty(credential.Password))
{
// 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);
+ outCredential = new SafeFreeNegoCredentials(false, string.Empty, string.Empty, string.Empty);
}
else
{
- outCredential = new SafeFreeNegoCredentials(credential.UserName, credential.Password, credential.Domain);
+ outCredential = new SafeFreeNegoCredentials(ntlmOnly, credential.UserName, credential.Password, credential.Domain);
}
- return outCredential;
+ return outCredential;
}
internal static SecurityStatusPal InitializeSecurityContext(
@@ -109,13 +114,16 @@ internal static SecurityStatusPal InitializeSecurityContext(
throw new PlatformNotSupportedException(SR.net_nego_channel_binding_not_supported);
}
+ SecurityBuffer inSecurityBuffer = (inSecurityBufferArray != null && inSecurityBufferArray.Length != 0)
+ ? inSecurityBufferArray[0]
+ : null;
+
return EstablishSecurityContext(
(SafeFreeNegoCredentials)credentialsHandle,
ref securityContext,
- false,
spn,
requestedContextFlags,
- ((inSecurityBufferArray != null && inSecurityBufferArray.Length != 0) ? inSecurityBufferArray[0] : null),
+ inSecurityBuffer,
outSecurityBuffer,
ref contextFlags);
}
@@ -135,7 +143,7 @@ internal static SecurityStatusPal AcceptSecurityContext(
SecurityBuffer outSecurityBuffer,
ref ContextFlagsPal contextFlags)
{
- throw new PlatformNotSupportedException();
+ throw new PlatformNotSupportedException(SR.net_nego_server_not_supported);
}
internal static void ValidateImpersonationLevel(TokenImpersonationLevel impersonationLevel)
@@ -163,15 +171,36 @@ internal static int Encrypt(
ref byte[] output,
uint sequenceNumber)
{
- Debug.Assert(!isNtlm, "Encrypt: NTLM is not yet supported");
- SafeDeleteNegoContext gssContext = (SafeDeleteNegoContext) securityContext;
- byte[] tempOutput = Interop.GssApi.Encrypt(gssContext.GssContext, isConfidential, buffer, offset, count);
-
- // 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);
- int resultSize = tempOutput.Length;
+ const int prefixLength = sizeof(uint); // Output bytes are preceded by length
+ SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
+ Debug.Assert((isNtlm == negoContext.IsNtlm), "Inconsistent NTLM parameter");
+ int resultSize;
+ if (null != negoContext.GssContext)
+ {
+ byte[] tempOutput = Interop.GssApi.Encrypt(negoContext.GssContext, isConfidential, buffer, offset, count);
+ output = new byte[tempOutput.Length + prefixLength];
+ Array.Copy(tempOutput, 0, output, prefixLength, tempOutput.Length);
+ resultSize = tempOutput.Length;
+ }
+ else
+ {
+ byte[] signature = negoContext.MakeClientSignature(buffer, offset, count);
+ if (isConfidential)
+ {
+ byte[] cipher = negoContext.Encrypt(buffer, offset, count);
+ output = new byte[cipher.Length + signature.Length + prefixLength];
+ Array.Copy(signature, 0, output, prefixLength, signature.Length);
+ Array.Copy(cipher, 0, output, signature.Length + prefixLength, cipher.Length);
+ }
+ else
+ {
+ output = new byte[count + signature.Length + prefixLength];
+ Array.Copy(signature, 0, output, prefixLength, signature.Length);
+ Array.Copy(buffer, offset, output, signature.Length + prefixLength, count);
+ }
+
+ resultSize = output.Length;
+ }
unchecked
{
output[0] = (byte)((resultSize) & 0xFF);
@@ -217,8 +246,24 @@ internal static int Decrypt(
throw new ArgumentOutOfRangeException("count");
}
- newOffset = offset;
- return Interop.GssApi.Decrypt(((SafeDeleteNegoContext)securityContext).GssContext, buffer, offset, count);
+ SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
+ Debug.Assert((isNtlm == negoContext.IsNtlm), "Inconsistent NTLM parameter");
+
+ newOffset = isNtlm ? (offset + NtlmSignatureLength) : offset;
+ if (!isConfidential)
+ {
+ return VerifySignature(negoContext, buffer, offset, count);
+ }
+
+ if (null != negoContext.GssContext)
+ {
+ return Interop.GssApi.Decrypt(negoContext.GssContext, buffer, offset, count);
+ }
+ else
+ {
+ int tempOffset;
+ return DecryptNtlm(negoContext, buffer, offset, count, isConfidential, out tempOffset, sequenceNumber);
+ }
}
internal static int DecryptNtlm(
@@ -230,57 +275,132 @@ internal static int DecryptNtlm(
out int newOffset,
uint sequenceNumber)
{
- throw new PlatformNotSupportedException(SR.net_nego_ntlm_not_supported);
+ SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)securityContext;
+ newOffset = offset + NtlmSignatureLength;
+ byte[] message = negoContext.Decrypt(buffer, newOffset, (count - NtlmSignatureLength));
+ Array.Copy(message, 0, buffer, newOffset, message.Length);
+ return VerifySignature(negoContext, buffer, offset, count);
+ }
+
+ private static int VerifySignature(SafeDeleteNegoContext negoContext, byte[] buffer, int offset, int count)
+ {
+ if (null != negoContext.GssContext)
+ {
+ return Interop.GssApi.Decrypt(negoContext.GssContext, buffer, offset, count);
+ }
+
+ count -= NtlmSignatureLength;
+ byte[] signature = negoContext.MakeServerSignature(buffer, offset + NtlmSignatureLength, count);
+ for (int i = 0; i < signature.Length; i++)
+ {
+ if (buffer[offset + i] != signature[i])
+ {
+ throw new Exception("Invalid signature");
+ }
+ }
+
+ return count;
}
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");
+ bool isNtlm;
+ SafeDeleteNegoContext negoContext;
+ SafeGssContextHandle contextHandle = null;
if (context == null)
{
- context = new SafeDeleteNegoContext(credential, targetName);
+ isNtlm = credential.IsNtlm || string.IsNullOrWhiteSpace(targetName);
+ negoContext = isNtlm ? null : new SafeDeleteNegoContext(credential, targetName);
+ context = negoContext;
+ }
+ else
+ {
+ negoContext = (SafeDeleteNegoContext)context;
+ isNtlm = negoContext.IsNtlm;
+ contextHandle = negoContext.GssContext;
}
- SafeDeleteNegoContext negoContext = (SafeDeleteNegoContext)context;
try
{
- Interop.NetSecurityNative.GssFlags inputFlags = ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags);
- uint outputFlags;
- SafeGssContextHandle contextHandle = negoContext.GssContext;
- bool done = Interop.GssApi.EstablishSecurityContext(
- ref contextHandle,
- credential.GssCredential,
- isNtlm,
- negoContext.TargetName,
- inputFlags,
- ((inputBuffer != null) ? inputBuffer.token : null),
- out outputBuffer.token,
- out outputFlags);
+ bool done = false;
+ if (!isNtlm)
+ {
+ Interop.NetSecurityNative.GssFlags inputFlags =
+ ContextFlagsAdapterPal.GetInteropFromContextFlagsPal(inFlags);
+
+ try
+ {
+ uint outputFlags;
+ done = Interop.GssApi.EstablishSecurityContext(
+ ref contextHandle,
+ credential.GssCredential,
+ isNtlm,
+ negoContext.TargetName,
+ inputFlags,
+ ((inputBuffer != null) ? inputBuffer.token : null),
+ out outputBuffer.token,
+ out outputFlags);
+
+ Debug.Assert(outputBuffer.token != null, "Unexpected null buffer returned by GssApi");
+ outputBuffer.size = outputBuffer.token.Length;
+ outputBuffer.offset = 0;
+
+ outFlags =
+ ContextFlagsAdapterPal.GetContextFlagsPalFromInterop(
+ (Interop.NetSecurityNative.GssFlags)outputFlags);
+ Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
+
+ // Save the inner context handle for further calls to NetSecurity
+ if (null == negoContext.GssContext)
+ {
+ negoContext.SetGssContext(contextHandle, false);
+ }
+ }
+ catch
+ {
+ // If non-default credentials are available,
+ // we need to try NTLM authentication
+ if (credential.IsDefault)
+ {
+ throw;
+ }
+
+ isNtlm = true;
+ negoContext.Dispose();
+ context = null;
+
+ }
+ }
+
+ if (isNtlm)
+ {
+ done = EstablishNtlmSecurityContext(
+ credential,
+ ref context,
+ targetName,
+ inFlags,
+ inputBuffer,
+ outputBuffer,
+ ref outFlags);
+ }
Debug.Assert(outputBuffer.token != null, "Unexpected null buffer returned by GssApi");
outputBuffer.size = outputBuffer.token.Length;
outputBuffer.offset = 0;
- outFlags = ContextFlagsAdapterPal.GetContextFlagsPalFromInterop((Interop.NetSecurityNative.GssFlags)outputFlags);
- Debug.Assert(negoContext.GssContext == null || contextHandle == negoContext.GssContext);
-
- // Save the inner context handle for further calls to NetSecurity
- if (null == negoContext.GssContext)
- {
- negoContext.SetGssContext(contextHandle);
- }
- return done ? SecurityStatusPal.CompleteNeeded : SecurityStatusPal.ContinueNeeded;
+ return done
+ ? (isNtlm ? SecurityStatusPal.OK : SecurityStatusPal.CompleteNeeded)
+ : SecurityStatusPal.ContinueNeeded;
}
- catch(Exception ex)
+ catch (Exception ex)
{
//TODO (Issue #5890): Print exception until issue is fixed
Debug.Write("Exception Caught. - " + ex);