From 766613a9b9bae9e1ac4534a2d11628ed96d2629b Mon Sep 17 00:00:00 2001 From: Hemanth Kapila Date: Wed, 27 Jan 2016 17:43:27 +0530 Subject: [PATCH 1/8] Native shims for libheimntlm Introducing native shims for libheimntlm inorder to support NTLM --- .../Interop.HeimdalNtlm.cs | 42 +++ .../Interop.HeimdalNtlmException.cs | 36 +++ .../Interop.Ntlm.cs | 124 ++++++++ .../Interop.EVP.Cipher.cs | 11 + .../SafeHandles/NtlmSecuritySafeHandles.cs | 278 ++++++++++++++++++ .../pal_ntlmapi.cpp | 266 +++++++++++++++++ .../System.Net.Security.Native/pal_ntlmapi.h | 81 +++++ .../pal_evp_cipher.cpp | 10 + .../pal_evp_cipher.h | 17 ++ .../src/Resources/Strings.resx | 3 + 10 files changed, 868 insertions(+) create mode 100644 src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs create mode 100644 src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlmException.cs create mode 100644 src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs create mode 100644 src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs create mode 100644 src/Native/System.Net.Security.Native/pal_ntlmapi.cpp create mode 100644 src/Native/System.Net.Security.Native/pal_ntlmapi.h diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs new file mode 100644 index 000000000000..4241e15cb773 --- /dev/null +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs @@ -0,0 +1,42 @@ +// 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; + +internal static partial class Interop +{ + internal static class HeimdalNtlm + { + internal static byte[] CreateNegotiateMessage(uint flags) + { + SafeNtlmBufferHandle data; + int dataLen; + int status = NetSecurity.HeimNtlmEncodeType1(flags, out data, out dataLen); + NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("HeimNtlmEncodeType1 failed", status); + using (data) + { + return data.ToByteArray(dataLen,0); + } + } + + internal static byte[] CreateAuthenticateMessage(uint flags, string username, string password, string domain, + byte[] type2Data, int offset, int count, out byte[] sessionKey) + { + using (SafeNtlmType3Handle challengeMessage = new SafeNtlmType3Handle(type2Data, offset, count)) + { + return challengeMessage.GetResponse(flags, username, password, domain, out sessionKey); + } + } + + internal static void CreateKeys(byte[] sessionKey, out SafeNtlmKeyHandle serverSignKey, out SafeNtlmKeyHandle serverSealKey, out SafeNtlmKeyHandle clientSignKey, out SafeNtlmKeyHandle clientSealKey) + { + serverSignKey = new SafeNtlmKeyHandle(sessionKey, false, false); + serverSealKey = new SafeNtlmKeyHandle(sessionKey, false, true); + clientSignKey = new SafeNtlmKeyHandle(sessionKey, true, false); + clientSealKey = new SafeNtlmKeyHandle(sessionKey, true, true); + } + } +} + diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlmException.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlmException.cs new file mode 100644 index 000000000000..d9f333514fa7 --- /dev/null +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlmException.cs @@ -0,0 +1,36 @@ +// 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; + +internal static partial class Interop +{ + internal static partial class NetSecurity + { + internal sealed class HeimdalNtlmException : Exception + { + public HeimdalNtlmException(string message) : base(message) + { + } + + public HeimdalNtlmException(int error) + : base(SR.Format(SR.net_generic_heimntlm_operation_failed, error)) + { + HResult = error; + } + + public static void AssertOrThrowIfError(string message, int error) + { + if (error != 0) + { + var ex = new HeimdalNtlmException(error); + Debug.Fail(message + ": " + ex); + throw ex; + } + } + } + } +} + diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs new file mode 100644 index 000000000000..6b04be61f6d6 --- /dev/null +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs @@ -0,0 +1,124 @@ +// 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 System.Text; +using System.Diagnostics; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class NetSecurity + { + // The following constant is used in calculation of NTOWF2 + // ref: https://msdn.microsoft.com/en-us/library/cc236700.aspx + public const int MD5DigestLength = 16; + [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmFreeBuf")] + internal static extern int HeimNtlmFreeBuf(IntPtr bufferHandle); + + [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_CopyBuffer")] + internal static extern int CopyBuffer(SafeNtlmBufferHandle data, byte[] buffer, int capacity, int offset); + + [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmEncodeType1")] + internal static extern int HeimNtlmEncodeType1(uint flags, out SafeNtlmBufferHandle data, out int length); + + [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmDecodeType2")] + internal static extern int HeimNtlmDecodeType2(byte[] data, int offset, int count, out SafeNtlmType2Handle type2Handle); + + [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmFreeType2")] + internal static extern int HeimNtlmFreeType2(IntPtr type2Handle); + + [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmNtKey", CharSet = CharSet.Ansi)] + internal static extern int HeimNtlmNtKey(string password, out SafeNtlmBufferHandle key, out int length); + + [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmCalculateResponse", CharSet = CharSet.Ansi)] + internal static extern int HeimNtlmCalculateResponse( + bool isLM, + SafeNtlmBufferHandle key, + SafeNtlmType2Handle type2Handle, + string username, + string target, + byte[] baseSessionKey, + int baseSessionKeyLen, + out SafeNtlmBufferHandle answer, + out int ansLength); + + [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_CreateType3Message", CharSet = CharSet.Ansi)] + internal static extern int CreateType3Message( + SafeNtlmBufferHandle key, + SafeNtlmType2Handle type2Handle, + string username, + string domain, + uint flags, + SafeNtlmBufferHandle lmResponse, + SafeNtlmBufferHandle ntlmResponse, + byte [] baseSessionKey, + int baseSessionKeyLen, + out SafeNtlmBufferHandle sessionKey, + out int sessionKeyLen, + out SafeNtlmBufferHandle data, + out int dataLen + ); + + internal partial class NtlmFlags + { + internal const uint NTLMSSP_NEGOTIATE_UNICODE = 0x1; + internal const uint NTLMSSP_REQUEST_TARGET = 0x4; + internal const uint NTLMSSP_NEGOTIATE_SIGN = 0x10; + internal const uint NTLMSSP_NEGOTIATE_SEAL = 0x20; + internal const uint NTLMSSP_NEGOTIATE_NTLM = 0x200; + internal const uint NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x8000; + internal const uint NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x80000; + internal const uint NTLMSSP_NEGOTIATE_128 = 0x20000000; + internal const uint NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000; + } + + internal static byte[] EVPDigest(byte[] key, byte[] input, int inputlen, out uint outputlen) + { + //reference doc: http://msdn.microsoft.com/en-us/library/cc236700.aspx + byte[] output = new byte[Interop.Crypto.EVP_MAX_MD_SIZE]; + outputlen = 0; + using (SafeEvpMdCtxHandle ctx = Interop.Crypto.EvpMdCtxCreate(Interop.Crypto.EvpMd5())) + unsafe + { + fixed (byte *keyPtr = key, inPtr = input, outPtr = output) + { + Check(Interop.Crypto.EvpDigestUpdate(ctx, keyPtr, key.Length)); + Check(Interop.Crypto.EvpDigestUpdate(ctx, inPtr, inputlen)); + Check(Interop.Crypto.EvpDigestFinalEx(ctx, outPtr, ref outputlen)); + } + } + return output; + } + + internal static unsafe byte[] HMACDigest(byte* key, int keylen, byte* input, int inputlen, byte* prefix, int prefixlen, out int hashLength) + { + hashLength = 0; + byte[] output = new byte[Interop.Crypto.EVP_MAX_MD_SIZE]; + using (SafeHmacCtxHandle ctx = Interop.Crypto.HmacCreate(key, keylen, Interop.Crypto.EvpMd5())) + { + if (prefixlen > 0) + { + Check(Interop.Crypto.HmacUpdate(ctx, prefix, prefixlen)); + } + Check(Interop.Crypto.HmacUpdate(ctx, input, inputlen)); + fixed (byte* hashPtr = output) + { + Check(Interop.Crypto.HmacFinal(ctx, hashPtr, ref hashLength)); + } + } + return output; + } + + private static void Check(int result) + { + const int Success = 1; + if (result != Success) + { + throw Interop.Crypto.CreateOpenSslCryptographicException(); + } + } + } + +} diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Cipher.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Cipher.cs index 419944a8a98c..1ed6bd4a7c15 100644 --- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Cipher.cs +++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.EVP.Cipher.cs @@ -67,5 +67,16 @@ internal static extern unsafe bool EvpCipherFinalEx( [DllImport(Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpDes3Cbc")] internal static extern IntPtr EvpDes3Cbc(); + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpRc4")] + internal static extern IntPtr EvpRc4(); + + [DllImport(Interop.Libraries.CryptoNative, EntryPoint = "CryptoNative_EvpCipher")] + internal static unsafe extern int EvpCipher( + SafeEvpCipherCtxHandle ctx, + byte[] output, + byte* input, + int inl); + } } diff --git a/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs b/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs new file mode 100644 index 000000000000..a60fef25ed1d --- /dev/null +++ b/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs @@ -0,0 +1,278 @@ +// 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.Security.Authentication; +using System.Security.Authentication.ExtendedProtection; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; + +namespace Microsoft.Win32.SafeHandles +{ + /// + /// Wrapper around a ntlm_buf* + /// + internal sealed class SafeNtlmBufferHandle : SafeHandle + { + public SafeNtlmBufferHandle() + : base(IntPtr.Zero, true) + { + } + + public byte[] ToByteArray(int length, int offset) + { + Debug.Assert(length >= 0, "negative length of buffer"); + Debug.Assert(offset < length, "invalid offset for buffer"); + byte[] target = new byte[length]; + Interop.NetSecurity.CopyBuffer(this, target, capacity: length, offset: offset); + return target; + } + + public override bool IsInvalid + { + get { return handle == IntPtr.Zero; } + } + + protected override bool ReleaseHandle() + { + Interop.NetSecurity.HeimNtlmFreeBuf(handle); + SetHandle(IntPtr.Zero); + return true; + } + } + + /// + /// Wrapper around a session key used for signing + /// + internal sealed class SafeNtlmKeyHandle : SafeHandle + { + private GCHandle _gch; + private uint _digestLength; + private uint _sequenceNumber; + private bool _isSealingKey; + private SafeEvpCipherCtxHandle _cipherContext; + + // From MS_NLMP SIGNKEY at https://msdn.microsoft.com/en-us/library/cc236711.aspx + private const string s_keyMagic = "session key to {0}-to-{1} {2} key magic constant\0"; + private const string s_client = "client"; + private const string s_server = "server"; + private const string s_signing = "signing"; + private const string s_sealing = "sealing"; + + public SafeNtlmKeyHandle(byte[] key, bool isClient, bool isSealingKey) + : base(IntPtr.Zero, true) + { + string keyMagic = string.Format(s_keyMagic, isClient ? s_client : s_server, + isClient ? s_server : s_client, isSealingKey ? s_sealing : s_signing); + + byte[] magic = Encoding.UTF8.GetBytes(keyMagic); + + byte[] digest = Interop.NetSecurity.EVPDigest(key, magic, magic.Length, out _digestLength); + _isSealingKey = isSealingKey; + if (_isSealingKey) + { + _cipherContext = Interop.Crypto.EvpCipherCreate(Interop.Crypto.EvpRc4(), digest, null, 1); + } + _gch = GCHandle.Alloc(digest, GCHandleType.Pinned); + handle = _gch.AddrOfPinnedObject(); + } + + public override bool IsInvalid + { + get { return handle == IntPtr.Zero; } + } + + protected override void Dispose(bool disposing) + { + if (disposing && (null != _cipherContext)) + { + _cipherContext.Dispose(); + _cipherContext = null; + } + base.Dispose(disposing); + } + + protected override bool ReleaseHandle() + { + _gch.Free(); + SetHandle(IntPtr.Zero); + return true; + } + + public byte[] Sign(SafeNtlmKeyHandle sealingKey, byte[] buffer, int offset, int count) + { + Debug.Assert(!_isSealingKey, "Cannot sign with sealing key"); + Debug.Assert(offset >= 0 && offset < buffer.Length, "Cannot sign with invalid offset " + offset); + Debug.Assert((count + offset) <= buffer.Length, "Cannot sign with invalid count " + count); + + // reference for signing a message: https://msdn.microsoft.com/en-us/library/cc236702.aspx + const uint Version = 0x00000001; + const int ChecksumOffset = 4; + const int SequenceNumberOffset = 12; + const int HMacDigestLength = 8; + + byte[] output = new byte[Interop.NetSecurity.MD5DigestLength]; + Array.Clear(output, 0, output.Length); + byte[] hash; + unsafe + { + + fixed (byte* outPtr = output) + fixed (byte* bytePtr = buffer) + { + MarshalUint(outPtr, Version); // version + MarshalUint(outPtr + SequenceNumberOffset, _sequenceNumber); + int hashLength; + hash = Interop.NetSecurity.HMACDigest((byte*) handle.ToPointer(), (int)_digestLength, (bytePtr + offset), count, + outPtr + SequenceNumberOffset, ChecksumOffset, out hashLength); + Debug.Assert(hash != null && hashLength >= HMacDigestLength, "HMACDigest has a length of at least " + HMacDigestLength); + _sequenceNumber++; + } + } + + if ((sealingKey == null) || sealingKey.IsInvalid) + { + Array.Copy(hash, 0, output, ChecksumOffset, HMacDigestLength); + } + else + { + byte[] cipher = sealingKey.SealOrUnseal(true, hash, 0, HMacDigestLength); + Array.Copy(cipher, 0, output, ChecksumOffset, cipher.Length); + } + + return output; + } + + public byte[] SealOrUnseal(bool seal, byte[] buffer, int offset, int count) + { + //Message Confidentiality. Reference: https://msdn.microsoft.com/en-us/library/cc236707.aspx + Debug.Assert(_isSealingKey, "Cannot seal or unseal with signing key"); + Debug.Assert(offset >= 0 && offset < buffer.Length, "Cannot sign with invalid offset " + offset); + Debug.Assert((count + offset) <= buffer.Length, "Cannot sign with invalid count "); + + unsafe + { + fixed (byte* bytePtr = buffer) + { + // Since RC4 is XOR-based, encrypt or decrypt is relative to input data + // reference: https://msdn.microsoft.com/en-us/library/cc236707.aspx + byte[] output = new byte[count]; + + Interop.Crypto.EvpCipher(_cipherContext, output, (bytePtr + offset), count); + return output; + + } + } + } + + private static unsafe void MarshalUint(byte* ptr, uint num) + { + for (int i = 0; i < 4; i++) + { + ptr[i] = (byte) (num & 0xff); + num >>= 8; + } + } + } + + /// + /// Wrapper around a ntlm_type2* + /// + internal sealed class SafeNtlmType2Handle : SafeHandle + { + public override bool IsInvalid + { + get { return handle == IntPtr.Zero; } + } + + protected override bool ReleaseHandle() + { + Interop.NetSecurity.HeimNtlmFreeType2(handle); + SetHandle(IntPtr.Zero); + return true; + } + + private SafeNtlmType2Handle() : base(IntPtr.Zero, true) + { + } + } + + /// + /// Wrapper around a ntlm_type3* + /// + internal sealed class SafeNtlmType3Handle : SafeHandle + { + private SafeNtlmType2Handle _type2Handle; + public SafeNtlmType3Handle(byte[] type2Data, int offset, int count) : base(IntPtr.Zero, true) + { + int status = Interop.NetSecurity.HeimNtlmDecodeType2(type2Data, offset, count, out _type2Handle); + Interop.NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("HeimNtlmDecodeType2 failed", status); + } + + public override bool IsInvalid + { + get { return (null != _type2Handle) && !_type2Handle.IsInvalid; } + } + + public byte[] GetResponse(uint flags, string username, string password, string domain, + out byte[] sessionKey) + { + // reference for NTLM response: https://msdn.microsoft.com/en-us/library/cc236700.aspx + sessionKey = null; + SafeNtlmBufferHandle key; + int keyLen; + int status = Interop.NetSecurity.HeimNtlmNtKey(password, out key, out keyLen); + Interop.NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("HeimNtlmKey failed", status); + + using (key) + { + byte[] baseSessionKey = new byte[Interop.NetSecurity.MD5DigestLength]; + SafeNtlmBufferHandle lmResponse; + int lmResponseLength; + + status = Interop.NetSecurity.HeimNtlmCalculateResponse(true, key, _type2Handle, username, domain, + baseSessionKey, baseSessionKey.Length, out lmResponse, out lmResponseLength); + Interop.NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("HeimNtlmCalculateResponse lm1 failed",status); + + SafeNtlmBufferHandle ntResponse; + int ntResponseLength; + status = Interop.NetSecurity.HeimNtlmCalculateResponse(false, key, _type2Handle, username, domain, + baseSessionKey, baseSessionKey.Length, out ntResponse, out ntResponseLength); + Interop.NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("HeimNtlmCalculateResponse lm2 failed", status); + + SafeNtlmBufferHandle sessionKeyHandle = null; + int sessionKeyLen; + SafeNtlmBufferHandle outputData = null; + int outputDataLen = 0; + status = Interop.NetSecurity.CreateType3Message(key, _type2Handle, username, domain, flags, lmResponse, ntResponse, baseSessionKey, + baseSessionKey.Length, out sessionKeyHandle, out sessionKeyLen, out outputData, + out outputDataLen); + Interop.NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("CreateType3Message failed", status); + using (sessionKeyHandle) + using (outputData) + { + sessionKey = sessionKeyHandle.ToByteArray(sessionKeyLen,0); + return outputData.ToByteArray(outputDataLen, 0); + } + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _type2Handle.Dispose(); + _type2Handle = null; + } + base.Dispose(disposing); + } + + protected override bool ReleaseHandle() + { + return true; + } + } +} diff --git a/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp b/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp new file mode 100644 index 000000000000..3f5361c66650 --- /dev/null +++ b/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp @@ -0,0 +1,266 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include +#include +#include +#include "pal_types.h" +#include "pal_ntlmapi.h" +#include "pal_utilities.h" +#include +#include +#include +#include "heimntlm.h" +#include "openssl/hmac.h" +#include "openssl/evp.h" + +static_assert(PAL_NTLMSSP_NEGOTIATE_UNICODE == NTLM_NEG_UNICODE, ""); +static_assert(PAL_NTLMSSP_REQUEST_TARGET == NTLM_NEG_TARGET, ""); +static_assert(PAL_NTLMSSP_NEGOTIATE_SIGN == NTLM_NEG_SIGN, ""); +static_assert(PAL_NTLMSSP_NEGOTIATE_SEAL == NTLM_NEG_SEAL, ""); +static_assert(PAL_NTLMSSP_NEGOTIATE_NTLM == NTLM_NEG_NTLM, ""); +static_assert(PAL_NTLMSSP_NEGOTIATE_ALWAYS_SIGN == NTLM_NEG_ALWAYS_SIGN, ""); +static_assert(PAL_NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY == NTLM_NEG_NTLM2_SESSION, ""); +static_assert(PAL_NTLMSSP_NEGOTIATE_128 == NTLM_ENC_128, ""); +static_assert(PAL_NTLMSSP_NEGOTIATE_KEY_EXCH == NTLM_NEG_KEYEX, ""); + +const int32_t MD5_DIGEST_LENGTH = 16; + +static inline int32_t NetSecurity_SetBufferLength(int32_t status, ntlm_buf** bufferHandle, int32_t* outLength) +{ + if (status != 0) + { + delete *bufferHandle; + *bufferHandle = nullptr; + *outLength = 0; + return status; + } + *outLength = static_cast((*bufferHandle)->length); + return status; +} + +extern "C" void NetSecurity_HeimNtlmFreeBuf(ntlm_buf* data) +{ + assert(data != nullptr); + heim_ntlm_free_buf(data); + delete data; +} + +extern "C" void NetSecurity_CopyBuffer(const ntlm_buf* bufferHandle, uint8_t* bytes, uint32_t capacity, uint32_t offset) +{ + if (bufferHandle == nullptr) + { + return; + } + assert(bufferHandle->length <= (capacity - offset)); + memcpy(bytes + UnsignedCast(offset), bufferHandle->data, bufferHandle->length); +} + +extern "C" int32_t NetSecurity_HeimNtlmEncodeType1(uint32_t flags, ntlm_buf** outBufferHandle, int32_t* outLength) +{ + assert(outBufferHandle != nullptr); + ntlm_type1 type1; + memset(&type1, 0, sizeof(ntlm_type1)); + type1.flags = flags; + *outBufferHandle = new ntlm_buf(); + return NetSecurity_SetBufferLength(heim_ntlm_encode_type1(&type1, *outBufferHandle), outBufferHandle, outLength); +} + +extern "C" int32_t NetSecurity_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2) +{ + assert(data != nullptr); + assert(offset >= 0); + assert(type2 != nullptr); + ntlm_buf buffer{.length = UnsignedCast(count), .data = data + offset}; + *type2 = new ntlm_type2(); + int32_t stat = heim_ntlm_decode_type2(&buffer, *type2); + if (stat != 0) + { + delete *type2; + *type2 = nullptr; + } + return stat; +} + +extern "C" void NetSecurity_HeimNtlmFreeType2(ntlm_type2* type2) +{ + assert(type2 != nullptr); + heim_ntlm_free_type2(type2); + delete type2; +} + +extern "C" int32_t NetSecurity_HeimNtlmNtKey(const char* password, ntlm_buf** outBufferHandle, int32_t* outLength) +{ + assert(outBufferHandle != nullptr); + *outBufferHandle = new ntlm_buf(); + return NetSecurity_SetBufferLength(heim_ntlm_nt_key(password, *outBufferHandle), outBufferHandle, outLength); +} + +extern "C" int32_t NetSecurity_HeimNtlmCalculateResponse(int32_t isLM, + const ntlm_buf* key, + ntlm_type2* type2, + char* username, + char* target, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + ntlm_buf** outBufferHandle, + int32_t* outLength) +{ + // reference doc: http://msdn.microsoft.com/en-us/library/cc236700.aspx + assert(baseSessionKeyLen == MD5_DIGEST_LENGTH); + assert(isLM == 0 || isLM == 1); + assert(type2 != nullptr); + assert(key != nullptr); + assert(outBufferHandle != nullptr); + *outBufferHandle = new ntlm_buf(); + if (isLM) + { + return NetSecurity_SetBufferLength( + heim_ntlm_calculate_lm2( + key->data, key->length, username, target, type2->challenge, baseSessionKey, *outBufferHandle), + outBufferHandle, + outLength); + } + else + { + if (type2->targetinfo.length == 0) + { + return NetSecurity_SetBufferLength( + heim_ntlm_calculate_ntlm1(key->data, key->length, type2->challenge, *outBufferHandle), + outBufferHandle, + outLength); + } + else + { + return NetSecurity_SetBufferLength(heim_ntlm_calculate_ntlm2(key->data, + key->length, + username, + target, + type2->challenge, + &type2->targetinfo, + baseSessionKey, + *outBufferHandle), + outBufferHandle, + outLength); + } + } +} + +static uint8_t* NetSecurity_HMACDigest(uint8_t* key, int32_t keylen, void* input, size_t inputlen) +{ + HMAC_CTX ctx; + uint8_t* output = new uint8_t[16]; + + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, key, keylen, EVP_md5(), nullptr); + HMAC_Update(&ctx, static_cast(input), inputlen); + uint32_t hashLength; + HMAC_Final(&ctx, output, &hashLength); + HMAC_CTX_cleanup(&ctx); + return output; +} + +static uint8_t* NetSecurity_EVPEncrypt(uint8_t* key, void* input, size_t inputlen) +{ + EVP_CIPHER_CTX ctx; + EVP_CIPHER_CTX_init(&ctx); + EVP_CipherInit_ex(&ctx, EVP_rc4(), nullptr, key, nullptr, 1); + + uint8_t* output = new uint8_t[inputlen]; + EVP_Cipher(&ctx, output, static_cast(input), static_cast(inputlen)); + + EVP_CIPHER_CTX_cleanup(&ctx); + return output; +} + +static int32_t +NetSecurity_build_ntlm2_master(uint8_t* key, int32_t keylen, ntlm_buf* blob, ntlm_buf* sessionKey, ntlm_buf* masterKey) +{ + // reference: https://msdn.microsoft.com/en-us/library/cc236709.aspx + uint8_t* ntlmv2hash = NetSecurity_HMACDigest(key, keylen, blob->data, blob->length); + int32_t status = heim_ntlm_build_ntlm1_master(ntlmv2hash, UnsignedCast(keylen), sessionKey, masterKey); + if (status) + { + delete[] ntlmv2hash; + return status; + } + + uint8_t* exportKey = NetSecurity_EVPEncrypt(ntlmv2hash, sessionKey->data, sessionKey->length); + delete[] ntlmv2hash; + masterKey->length = sessionKey->length; + masterKey->data = exportKey; + return status; +} + +extern "C" int32_t NetSecurity_CreateType3Message(ntlm_buf* key, + ntlm_type2* type2, + const char* username, + const char* domain, + uint32_t flags, + ntlm_buf* lmResponse, + ntlm_buf* ntlmResponse, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + ntlm_buf** outSessionHandle, + int* outSessionKeyLen, + ntlm_buf** outBufferHandle, + int32_t* outLength) +{ + assert(key != nullptr); + assert(type2 != nullptr); + assert(lmResponse != nullptr); + assert(ntlmResponse != nullptr); + static char* workstation = static_cast(calloc(1, sizeof(char))); // empty string + ntlm_type3 type3; + memset(&type3, 0, sizeof(ntlm_type3)); + type3.username = const_cast(username); + type3.targetname = const_cast(domain); + type3.lm = *lmResponse; + type3.ntlm = *ntlmResponse; + type3.ws = workstation; + type3.flags = flags; + + int32_t status = 0; + ntlm_buf masterKey = {.length = 0, .data = nullptr}; + *outSessionHandle = new ntlm_buf(); + + if (type2->targetinfo.length == 0) + { + status = heim_ntlm_build_ntlm1_master(key->data, key->length, *outSessionHandle, &masterKey); + } + else + { + // Only first 16 bytes of the NTLMv2 response should be passed + assert(ntlmResponse->length >= MD5_DIGEST_LENGTH); + ntlm_buf blob = {.length = MD5_DIGEST_LENGTH, .data = ntlmResponse->data}; + status = + NetSecurity_build_ntlm2_master(baseSessionKey, baseSessionKeyLen, &blob, *outSessionHandle, &masterKey); + } + status = NetSecurity_SetBufferLength(status, outSessionHandle, outSessionKeyLen); + if (status != 0) + { + return status; + } + + *outBufferHandle = new ntlm_buf(); + type3.sessionkey = masterKey; + status = heim_ntlm_encode_type3(&type3, *outBufferHandle); + if (status != 0) + { + heim_ntlm_free_buf(*outSessionHandle); + delete *outSessionHandle; + *outSessionKeyLen = 0; + } + + if (type2->targetinfo.length == 0) + { + heim_ntlm_free_buf(&masterKey); + } + else + { + // in case of v2, masterKey.data is created by NetSecurity_build_ntlm2_master function and free_buf cannot be + // called. + delete[] static_cast(masterKey.data); + } + return NetSecurity_SetBufferLength(status, outBufferHandle, outLength); +} diff --git a/src/Native/System.Net.Security.Native/pal_ntlmapi.h b/src/Native/System.Net.Security.Native/pal_ntlmapi.h new file mode 100644 index 000000000000..125e1eeecfea --- /dev/null +++ b/src/Native/System.Net.Security.Native/pal_ntlmapi.h @@ -0,0 +1,81 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#pragma once + +enum NtlmFlags : int32_t +{ + PAL_NTLMSSP_NEGOTIATE_UNICODE = 0x1, + PAL_NTLMSSP_REQUEST_TARGET = 0x4, + PAL_NTLMSSP_NEGOTIATE_SIGN = 0x10, + PAL_NTLMSSP_NEGOTIATE_SEAL = 0x20, + PAL_NTLMSSP_NEGOTIATE_NTLM = 0x200, + PAL_NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x8000, + PAL_NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x80000, + PAL_NTLMSSP_NEGOTIATE_128 = 0x20000000, + PAL_NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000, +}; + +struct ntlm_buf; +struct ntlm_type2; + +/* +Shims heim_ntlm_free_buf method. +*/ +extern "C" void NetSecurity_HeimNtlmFreeBuf(ntlm_buf* data); + +/* +Copies data from ntlm_buffer into an array of given size, from the offset. +*/ +extern "C" void +NetSecurity_CopyBuffer(const ntlm_buf* bufferHandle, uint8_t* bytes, uint32_t capacity, uint32_t offset); + +/* +Shims heim_ntlm_encode_type1 method. +*/ +extern "C" int32_t NetSecurity_HeimNtlmEncodeType1(uint32_t flags, ntlm_buf** outBufferHandle, int* outLength); + +/* +Shims heim_ntlm_decode_type2 method. +*/ +extern "C" int32_t NetSecurity_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2); + +/* +Shims heim_ntlm_free_type2 method. +*/ +extern "C" void NetSecurity_HeimNtlmFreeType2(ntlm_type2* type2); + +/* +Shims heim_ntlm_nt_key method. +*/ +extern "C" int32_t NetSecurity_HeimNtlmNtKey(const char* password, ntlm_buf** outBufferHandle, int* outLength); + +/* +Shims heim_ntlm_calculate_lm2/_ntlm2 methods. +*/ +extern "C" int32_t NetSecurity_HeimNtlmCalculateResponse(int32_t isLM, + const ntlm_buf* key, + ntlm_type2* type2, + char* username, + char* target, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + ntlm_buf** data, + int* outLength); + +/* +Implements Type3 msg proccessing logic +*/ +extern "C" int32_t NetSecurity_CreateType3Message(ntlm_buf* key, + ntlm_type2* type2, + const char* username, + const char* domain, + uint32_t flags, + ntlm_buf* lmResponse, + ntlm_buf* ntlmResponse, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + ntlm_buf** outSessionHandle, + int32_t* outSessionKeyLen, + ntlm_buf** outBufferHandle, + int32_t* outLength); diff --git a/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.cpp b/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.cpp index 26ead2bb5698..67f81e270129 100644 --- a/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.cpp +++ b/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.cpp @@ -123,3 +123,13 @@ extern "C" const EVP_CIPHER* CryptoNative_EvpDes3Cbc() { return EVP_des_ede3_cbc(); } + +extern "C" const EVP_CIPHER* CryptoNative_EvpRc4() +{ + return EVP_rc4(); +} + +extern "C" void CryptoNative_EvpCipher(EVP_CIPHER_CTX* ctx, uint8_t* out, const uint8_t *in, uint32_t inl) +{ + EVP_Cipher(ctx, out, in, inl); +} diff --git a/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.h b/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.h index db822f0d7237..b42215352bac 100644 --- a/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.h +++ b/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.h @@ -127,3 +127,20 @@ EvpDes3Cbc Direct shim to EVP_des_ede3_cbc. */ extern "C" const EVP_CIPHER* CryptoNative_EvpDes3Cbc(); + +/* +Function: +EvpRc4 + +Direct shim to EVP_rc4. +*/ +extern "C" const EVP_CIPHER* CryptoNative_EvpRc4(); + +/* +Function: +EvpCipher + +Direct shim to EVP_Cipher. +*/ +extern "C" +void CryptoNative_EvpCipher(EVP_CIPHER_CTX* ctx, uint8_t *out, const uint8_t *in, uint32_t inl); diff --git a/src/System.Net.Security/src/Resources/Strings.resx b/src/System.Net.Security/src/Resources/Strings.resx index 3634df93afa9..9486f39f697a 100644 --- a/src/System.Net.Security/src/Resources/Strings.resx +++ b/src/System.Net.Security/src/Resources/Strings.resx @@ -441,4 +441,7 @@ Server implementation is not supported + + Heimntlm operation failed with status: {0}) + From 3acbe57dd215b57d420cbbb0d956632c397a755a Mon Sep 17 00:00:00 2001 From: kapilash Date: Fri, 29 Jan 2016 13:23:29 +0530 Subject: [PATCH 2/8] PR Feedback Use IncrementalHash instead of implementing it Use Names consistent with the other PR Conflicts: src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs --- .../Interop.Ntlm.cs | 68 +++---------------- .../SafeHandles/NtlmSecuritySafeHandles.cs | 51 ++++++-------- .../pal_ntlmapi.cpp | 5 +- .../System.Net.Security.Native/pal_ntlmapi.h | 2 +- 4 files changed, 37 insertions(+), 89 deletions(-) diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs index 6b04be61f6d6..56847276c347 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs @@ -2,9 +2,10 @@ // 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.Security.Cryptography; using System.Text; -using System.Diagnostics; using Microsoft.Win32.SafeHandles; internal static partial class Interop @@ -14,25 +15,25 @@ internal static partial class NetSecurity // The following constant is used in calculation of NTOWF2 // ref: https://msdn.microsoft.com/en-us/library/cc236700.aspx public const int MD5DigestLength = 16; - [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmFreeBuf")] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmFreeBuf")] internal static extern int HeimNtlmFreeBuf(IntPtr bufferHandle); - [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_CopyBuffer")] - internal static extern int CopyBuffer(SafeNtlmBufferHandle data, byte[] buffer, int capacity, int offset); + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_ExtractNtlmBuffer")] + internal static extern int ExtractNtlmBuffer(SafeNtlmBufferHandle data, byte[] buffer, int capacity, int offset); - [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmEncodeType1")] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmEncodeType1")] internal static extern int HeimNtlmEncodeType1(uint flags, out SafeNtlmBufferHandle data, out int length); - [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmDecodeType2")] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmDecodeType2")] internal static extern int HeimNtlmDecodeType2(byte[] data, int offset, int count, out SafeNtlmType2Handle type2Handle); - [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmFreeType2")] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmFreeType2")] internal static extern int HeimNtlmFreeType2(IntPtr type2Handle); - [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmNtKey", CharSet = CharSet.Ansi)] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmNtKey", CharSet = CharSet.Ansi)] internal static extern int HeimNtlmNtKey(string password, out SafeNtlmBufferHandle key, out int length); - [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_HeimNtlmCalculateResponse", CharSet = CharSet.Ansi)] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmCalculateResponse", CharSet = CharSet.Ansi)] internal static extern int HeimNtlmCalculateResponse( bool isLM, SafeNtlmBufferHandle key, @@ -44,7 +45,7 @@ internal static extern int HeimNtlmCalculateResponse( out SafeNtlmBufferHandle answer, out int ansLength); - [DllImport(Interop.Libraries.SecurityNative, EntryPoint="NetSecurity_CreateType3Message", CharSet = CharSet.Ansi)] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_CreateType3Message", CharSet = CharSet.Ansi)] internal static extern int CreateType3Message( SafeNtlmBufferHandle key, SafeNtlmType2Handle type2Handle, @@ -73,52 +74,5 @@ internal partial class NtlmFlags internal const uint NTLMSSP_NEGOTIATE_128 = 0x20000000; internal const uint NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000; } - - internal static byte[] EVPDigest(byte[] key, byte[] input, int inputlen, out uint outputlen) - { - //reference doc: http://msdn.microsoft.com/en-us/library/cc236700.aspx - byte[] output = new byte[Interop.Crypto.EVP_MAX_MD_SIZE]; - outputlen = 0; - using (SafeEvpMdCtxHandle ctx = Interop.Crypto.EvpMdCtxCreate(Interop.Crypto.EvpMd5())) - unsafe - { - fixed (byte *keyPtr = key, inPtr = input, outPtr = output) - { - Check(Interop.Crypto.EvpDigestUpdate(ctx, keyPtr, key.Length)); - Check(Interop.Crypto.EvpDigestUpdate(ctx, inPtr, inputlen)); - Check(Interop.Crypto.EvpDigestFinalEx(ctx, outPtr, ref outputlen)); - } - } - return output; - } - - internal static unsafe byte[] HMACDigest(byte* key, int keylen, byte* input, int inputlen, byte* prefix, int prefixlen, out int hashLength) - { - hashLength = 0; - byte[] output = new byte[Interop.Crypto.EVP_MAX_MD_SIZE]; - using (SafeHmacCtxHandle ctx = Interop.Crypto.HmacCreate(key, keylen, Interop.Crypto.EvpMd5())) - { - if (prefixlen > 0) - { - Check(Interop.Crypto.HmacUpdate(ctx, prefix, prefixlen)); - } - Check(Interop.Crypto.HmacUpdate(ctx, input, inputlen)); - fixed (byte* hashPtr = output) - { - Check(Interop.Crypto.HmacFinal(ctx, hashPtr, ref hashLength)); - } - } - return output; - } - - private static void Check(int result) - { - const int Success = 1; - if (result != Success) - { - throw Interop.Crypto.CreateOpenSslCryptographicException(); - } - } } - } diff --git a/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs b/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs index a60fef25ed1d..d2ad88d0b653 100644 --- a/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs +++ b/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs @@ -27,7 +27,7 @@ public byte[] ToByteArray(int length, int offset) Debug.Assert(length >= 0, "negative length of buffer"); Debug.Assert(offset < length, "invalid offset for buffer"); byte[] target = new byte[length]; - Interop.NetSecurity.CopyBuffer(this, target, capacity: length, offset: offset); + Interop.NetSecurity.ExtractNtlmBuffer(this, target, capacity: length, offset: offset); return target; } @@ -49,10 +49,9 @@ protected override bool ReleaseHandle() /// internal sealed class SafeNtlmKeyHandle : SafeHandle { - private GCHandle _gch; - private uint _digestLength; private uint _sequenceNumber; - private bool _isSealingKey; + private readonly bool _isSealingKey; + private readonly byte[] _digest; private SafeEvpCipherCtxHandle _cipherContext; // From MS_NLMP SIGNKEY at https://msdn.microsoft.com/en-us/library/cc236711.aspx @@ -68,21 +67,23 @@ public SafeNtlmKeyHandle(byte[] key, bool isClient, bool isSealingKey) string keyMagic = string.Format(s_keyMagic, isClient ? s_client : s_server, isClient ? s_server : s_client, isSealingKey ? s_sealing : s_signing); - byte[] magic = Encoding.UTF8.GetBytes(keyMagic); + using (IncrementalHash incremental = IncrementalHash.CreateHash(HashAlgorithmName.MD5)) + { + incremental.AppendData(key); + incremental.AppendData(Encoding.UTF8.GetBytes(keyMagic)); + _digest = incremental.GetHashAndReset(); + } - byte[] digest = Interop.NetSecurity.EVPDigest(key, magic, magic.Length, out _digestLength); _isSealingKey = isSealingKey; if (_isSealingKey) { - _cipherContext = Interop.Crypto.EvpCipherCreate(Interop.Crypto.EvpRc4(), digest, null, 1); + _cipherContext = Interop.Crypto.EvpCipherCreate(Interop.Crypto.EvpRc4(), _digest, null, 1); } - _gch = GCHandle.Alloc(digest, GCHandleType.Pinned); - handle = _gch.AddrOfPinnedObject(); } public override bool IsInvalid { - get { return handle == IntPtr.Zero; } + get { return true; } } protected override void Dispose(bool disposing) @@ -97,8 +98,6 @@ protected override void Dispose(bool disposing) protected override bool ReleaseHandle() { - _gch.Free(); - SetHandle(IntPtr.Zero); return true; } @@ -116,24 +115,19 @@ public byte[] Sign(SafeNtlmKeyHandle sealingKey, byte[] buffer, int offset, int byte[] output = new byte[Interop.NetSecurity.MD5DigestLength]; Array.Clear(output, 0, output.Length); + MarshalUint(output, 0, Version); // version + MarshalUint(output, SequenceNumberOffset, _sequenceNumber); byte[] hash; - unsafe - { - fixed (byte* outPtr = output) - fixed (byte* bytePtr = buffer) - { - MarshalUint(outPtr, Version); // version - MarshalUint(outPtr + SequenceNumberOffset, _sequenceNumber); - int hashLength; - hash = Interop.NetSecurity.HMACDigest((byte*) handle.ToPointer(), (int)_digestLength, (bytePtr + offset), count, - outPtr + SequenceNumberOffset, ChecksumOffset, out hashLength); - Debug.Assert(hash != null && hashLength >= HMacDigestLength, "HMACDigest has a length of at least " + HMacDigestLength); - _sequenceNumber++; - } + using (var incremental = IncrementalHash.CreateHMAC(HashAlgorithmName.MD5, _digest)) + { + incremental.AppendData(output, SequenceNumberOffset, ChecksumOffset); + incremental.AppendData(buffer, offset, count); + hash = incremental.GetHashAndReset(); + _sequenceNumber++; } - if ((sealingKey == null) || sealingKey.IsInvalid) + if (sealingKey == null) { Array.Copy(hash, 0, output, ChecksumOffset, HMacDigestLength); } @@ -163,16 +157,15 @@ public byte[] SealOrUnseal(bool seal, byte[] buffer, int offset, int count) Interop.Crypto.EvpCipher(_cipherContext, output, (bytePtr + offset), count); return output; - } } } - private static unsafe void MarshalUint(byte* ptr, uint num) + private static void MarshalUint(byte[] ptr, int offset, uint num) { for (int i = 0; i < 4; i++) { - ptr[i] = (byte) (num & 0xff); + ptr[offset + i] = (byte) (num & 0xff); num >>= 8; } } diff --git a/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp b/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp index 3f5361c66650..6994cbd84322 100644 --- a/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp +++ b/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp @@ -46,14 +46,15 @@ extern "C" void NetSecurity_HeimNtlmFreeBuf(ntlm_buf* data) delete data; } -extern "C" void NetSecurity_CopyBuffer(const ntlm_buf* bufferHandle, uint8_t* bytes, uint32_t capacity, uint32_t offset) +extern "C" void NetSecurity_ExtractNtlmBuffer(const ntlm_buf* bufferHandle, uint8_t* destination, uint32_t capacity, uint32_t offset) { if (bufferHandle == nullptr) { return; } + assert(destination != nullptr); assert(bufferHandle->length <= (capacity - offset)); - memcpy(bytes + UnsignedCast(offset), bufferHandle->data, bufferHandle->length); + memcpy(destination + UnsignedCast(offset), bufferHandle->data, bufferHandle->length); } extern "C" int32_t NetSecurity_HeimNtlmEncodeType1(uint32_t flags, ntlm_buf** outBufferHandle, int32_t* outLength) diff --git a/src/Native/System.Net.Security.Native/pal_ntlmapi.h b/src/Native/System.Net.Security.Native/pal_ntlmapi.h index 125e1eeecfea..30a6678ad06f 100644 --- a/src/Native/System.Net.Security.Native/pal_ntlmapi.h +++ b/src/Native/System.Net.Security.Native/pal_ntlmapi.h @@ -28,7 +28,7 @@ extern "C" void NetSecurity_HeimNtlmFreeBuf(ntlm_buf* data); Copies data from ntlm_buffer into an array of given size, from the offset. */ extern "C" void -NetSecurity_CopyBuffer(const ntlm_buf* bufferHandle, uint8_t* bytes, uint32_t capacity, uint32_t offset); +NetSecurity_ExtractNtlmBuffer(const ntlm_buf* bufferHandle, uint8_t* destination, uint32_t capacity, uint32_t offset); /* Shims heim_ntlm_encode_type1 method. From a8f6f274bfebea6bdc9dab19f80099eedd33018d Mon Sep 17 00:00:00 2001 From: kapilash Date: Fri, 5 Feb 2016 01:49:12 +0530 Subject: [PATCH 3/8] PR Feedback Native Layer: asserts Introduced struct PAL_NtlmBuffer to have a clearer ownership of the resources owned by ntlm_buf Managed Layer: Cleaned up code that was a result of code evaluation. Refactored classes from SafeHandles to simple classes or Disposable objects. Proper handling of offsets in Debug.Asserts --- .../Interop.HeimdalNtlm.cs | 163 ++++++++++-- .../Interop.HeimdalNtlmException.cs | 10 +- .../Interop.Ntlm.cs | 49 ++-- .../Interop.NtlmBuffer.cs | 67 +++++ .../Interop.NtlmType3Message.cs | 99 +++++++ .../SafeHandles/NtlmSecuritySafeHandles.cs | 242 +----------------- .../pal_ntlmapi.cpp | 216 +++++++++------- .../System.Net.Security.Native/pal_ntlmapi.h | 69 +++-- 8 files changed, 496 insertions(+), 419 deletions(-) create mode 100644 src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmBuffer.cs create mode 100644 src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmType3Message.cs diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs index 4241e15cb773..cd110e9aa3de 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs @@ -1,41 +1,172 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// 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 System; +using System.Diagnostics; using System.Runtime.InteropServices; +using System.Security.Authentication; +using System.Security.Authentication.ExtendedProtection; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; using Microsoft.Win32.SafeHandles; internal static partial class Interop { - internal static class HeimdalNtlm + internal static partial class HeimdalNtlm { + private const string ClientToServerSigningMagicKey = "session key to client-to-server signing key magic constant\0"; + private const string ServerToClientSigningMagicKey = "session key to server-to-client signing key magic constant\0"; + private const string ServerToClientSealingMagicKey = "session key to server-to-client sealing key magic constant\0"; + private const string ClientToServerSealingMagicKey = "session key to client-to-server sealing key magic constant\0"; + + internal sealed class SigningKey + { + private uint _sequenceNumber; + private readonly byte[] _digest; + + public SigningKey(byte[] key, string magicKey) + { + using (IncrementalHash incremental = IncrementalHash.CreateHash(HashAlgorithmName.MD5)) + { + incremental.AppendData(key); + incremental.AppendData(Encoding.UTF8.GetBytes(magicKey)); + _digest = incremental.GetHashAndReset(); + } + } + + public byte[] Sign(SealingKey sealingKey, byte[] buffer, int offset, int count) + { + Debug.Assert(offset >= 0 && offset <= buffer.Length, "cannot sign with invalid offset"); + Debug.Assert(count <= buffer.Length - offset, "cannot sign with invalid count"); + + // reference for signing a message: https://msdn.microsoft.com/en-us/library/cc236702.aspx + const uint Version = 0x00000001; + const int ChecksumOffset = 4; + const int SequenceNumberOffset = 12; + const int HMacDigestLength = 8; + + byte[] output = new byte[Interop.NetSecurityNative.MD5DigestLength]; + MarshalUint(output, 0, Version); // version + MarshalUint(output, SequenceNumberOffset, _sequenceNumber); + byte[] hash; + + using (var incremental = IncrementalHash.CreateHMAC(HashAlgorithmName.MD5, _digest)) + { + incremental.AppendData(output, SequenceNumberOffset, ChecksumOffset); + incremental.AppendData(buffer, offset, count); + hash = incremental.GetHashAndReset(); + _sequenceNumber++; + } + + if (sealingKey == null) + { + Array.Copy(hash, 0, output, ChecksumOffset, HMacDigestLength); + } + else + { + byte[] cipher = sealingKey.SealOrUnseal(hash, 0, HMacDigestLength); + Array.Copy(cipher, 0, output, ChecksumOffset, cipher.Length); + } + + return output; + } + } + + internal sealed class SealingKey : IDisposable + { + private readonly byte[] _digest; + private SafeEvpCipherCtxHandle _cipherContext; + + public SealingKey(byte[] key, string magicKey) + { + _digest = NtlmKeyDigest(key, magicKey); + _cipherContext = Interop.Crypto.EvpCipherCreate(Interop.Crypto.EvpRc4(), _digest, null, 1); + } + + public byte[] SealOrUnseal(byte[] buffer, int offset, int count) + { + // Message Confidentiality. Reference: https://msdn.microsoft.com/en-us/library/cc236707.aspx + Debug.Assert(offset >= 0 && offset <= buffer.Length, "Cannot sign with invalid offset " + offset); + Debug.Assert(count >= 0, "cannot sign with invalid count"); + Debug.Assert(count <= (buffer.Length - offset), "Cannot sign with invalid count "); + + unsafe + { + fixed (byte *bytePtr = buffer) + { + // Since RC4 is XOR-based, encrypt or decrypt is relative to input data + // reference: https://msdn.microsoft.com/en-us/library/cc236707.aspx + byte[] output = new byte[count]; + + Interop.Crypto.EvpCipher(_cipherContext, output, (bytePtr + offset), count); + MockUtils.MockLogging.PrintInfo(null, "returning from seal"); + return output; + } + } + } + + public void Dispose() + { + if (_cipherContext != null) + { + _cipherContext.Dispose(); + _cipherContext = null; + } + } + } + + private static byte[] NtlmKeyDigest(byte[] key, string magicKey) + { + using (IncrementalHash incremental = IncrementalHash.CreateHash(HashAlgorithmName.MD5)) + { + incremental.AppendData(key); + incremental.AppendData(Encoding.UTF8.GetBytes(magicKey)); + return incremental.GetHashAndReset(); + } + } + + private static void MarshalUint(byte[] ptr, int offset, uint num) + { + for (int i = 0; i < 4; i++) + { + ptr[offset + i] = (byte) (num & 0xff); + num >>= 8; + } + } + internal static byte[] CreateNegotiateMessage(uint flags) { - SafeNtlmBufferHandle data; - int dataLen; - int status = NetSecurity.HeimNtlmEncodeType1(flags, out data, out dataLen); - NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("HeimNtlmEncodeType1 failed", status); - using (data) + NetSecurityNative.NtlmBuffer buffer = default(NetSecurityNative.NtlmBuffer); + try + { + int status = NetSecurityNative.HeimNtlmEncodeType1(flags, ref buffer); + NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + MockUtils.MockLogging.PrintInfo("kapilash", "created negotiate message"); + return buffer.ToByteArray(); + } + finally { - return data.ToByteArray(dataLen,0); + buffer.Dispose(); } } internal static byte[] CreateAuthenticateMessage(uint flags, string username, string password, string domain, byte[] type2Data, int offset, int count, out byte[] sessionKey) { - using (SafeNtlmType3Handle challengeMessage = new SafeNtlmType3Handle(type2Data, offset, count)) + using (NtlmType3Message challengeMessage = new NtlmType3Message(type2Data, offset, count)) { - return challengeMessage.GetResponse(flags, username, password, domain, out sessionKey); + return challengeMessage.GetResponse(flags, username, password, domain, out sessionKey); } } - internal static void CreateKeys(byte[] sessionKey, out SafeNtlmKeyHandle serverSignKey, out SafeNtlmKeyHandle serverSealKey, out SafeNtlmKeyHandle clientSignKey, out SafeNtlmKeyHandle clientSealKey) + internal static void CreateKeys(byte[] sessionKey, out SigningKey serverSignKey, out SealingKey serverSealKey, out SigningKey clientSignKey, out SealingKey clientSealKey) { - serverSignKey = new SafeNtlmKeyHandle(sessionKey, false, false); - serverSealKey = new SafeNtlmKeyHandle(sessionKey, false, true); - clientSignKey = new SafeNtlmKeyHandle(sessionKey, true, false); - clientSealKey = new SafeNtlmKeyHandle(sessionKey, true, true); + serverSignKey = new SigningKey(sessionKey, ServerToClientSigningMagicKey); + serverSealKey = new SealingKey(sessionKey, ServerToClientSealingMagicKey); + clientSignKey = new SigningKey(sessionKey, ClientToServerSigningMagicKey); + clientSealKey = new SealingKey(sessionKey, ClientToServerSealingMagicKey); } } } diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlmException.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlmException.cs index d9f333514fa7..4722e60e0b2e 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlmException.cs +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlmException.cs @@ -1,5 +1,6 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// 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 System; using System.Diagnostics; @@ -7,7 +8,7 @@ internal static partial class Interop { - internal static partial class NetSecurity + internal static partial class NetSecurityNative { internal sealed class HeimdalNtlmException : Exception { @@ -21,12 +22,11 @@ public HeimdalNtlmException(int error) HResult = error; } - public static void AssertOrThrowIfError(string message, int error) + public static void ThrowIfError(int error) { if (error != 0) { var ex = new HeimdalNtlmException(error); - Debug.Fail(message + ": " + ex); throw ex; } } diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs index 56847276c347..24f272e65871 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs @@ -1,5 +1,6 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// 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 System; using System.Diagnostics; @@ -10,57 +11,51 @@ internal static partial class Interop { - internal static partial class NetSecurity + internal static partial class NetSecurityNative { // The following constant is used in calculation of NTOWF2 - // ref: https://msdn.microsoft.com/en-us/library/cc236700.aspx + // reference: https://msdn.microsoft.com/en-us/library/cc236700.aspx public const int MD5DigestLength = 16; - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmFreeBuf")] - internal static extern int HeimNtlmFreeBuf(IntPtr bufferHandle); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_ExtractNtlmBuffer")] - internal static extern int ExtractNtlmBuffer(SafeNtlmBufferHandle data, byte[] buffer, int capacity, int offset); + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ReleaseNtlmBuffer")] + internal static extern int ReleaseNtlmBuffer(IntPtr bufferPtr, UInt64 length); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmEncodeType1")] - internal static extern int HeimNtlmEncodeType1(uint flags, out SafeNtlmBufferHandle data, out int length); + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_HeimNtlmEncodeType1")] + internal static extern int HeimNtlmEncodeType1(uint flags, ref NtlmBuffer buffer); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmDecodeType2")] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_HeimNtlmDecodeType2")] internal static extern int HeimNtlmDecodeType2(byte[] data, int offset, int count, out SafeNtlmType2Handle type2Handle); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmFreeType2")] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_HeimNtlmFreeType2")] internal static extern int HeimNtlmFreeType2(IntPtr type2Handle); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmNtKey", CharSet = CharSet.Ansi)] - internal static extern int HeimNtlmNtKey(string password, out SafeNtlmBufferHandle key, out int length); + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_HeimNtlmNtKey", CharSet = CharSet.Ansi)] + internal static extern int HeimNtlmNtKey(string password, ref NtlmBuffer key); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_HeimNtlmCalculateResponse", CharSet = CharSet.Ansi)] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_HeimNtlmCalculateResponse", CharSet = CharSet.Ansi)] internal static extern int HeimNtlmCalculateResponse( bool isLM, - SafeNtlmBufferHandle key, + ref NtlmBuffer key, SafeNtlmType2Handle type2Handle, string username, string target, byte[] baseSessionKey, int baseSessionKeyLen, - out SafeNtlmBufferHandle answer, - out int ansLength); + ref NtlmBuffer answer); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurity_CreateType3Message", CharSet = CharSet.Ansi)] + [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_CreateType3Message", CharSet = CharSet.Ansi)] internal static extern int CreateType3Message( - SafeNtlmBufferHandle key, + ref NtlmBuffer key, SafeNtlmType2Handle type2Handle, string username, string domain, uint flags, - SafeNtlmBufferHandle lmResponse, - SafeNtlmBufferHandle ntlmResponse, + ref NtlmBuffer lmResponse, + ref NtlmBuffer ntlmResponse, byte [] baseSessionKey, int baseSessionKeyLen, - out SafeNtlmBufferHandle sessionKey, - out int sessionKeyLen, - out SafeNtlmBufferHandle data, - out int dataLen - ); + ref NtlmBuffer sessionKey, + ref NtlmBuffer data); internal partial class NtlmFlags { diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmBuffer.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmBuffer.cs new file mode 100644 index 000000000000..98d77efd0bfc --- /dev/null +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmBuffer.cs @@ -0,0 +1,67 @@ +// 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 System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class NetSecurityNative + { + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct NtlmBuffer : IDisposable + { + internal UInt64 length; + internal IntPtr data; + + internal int Copy(byte[] destination, int offset) + { + Debug.Assert(destination != null, "target destination cannot be null"); + Debug.Assert(offset >= 0 && offset <= destination.Length, "invalid offset " + offset); + + if (data == IntPtr.Zero || length == 0) + { + return 0; + } + + int bufferLength = Convert.ToInt32(length); + int available = destination.Length - offset; // amount of space in the given buffer + if (bufferLength > available) + { + throw new NetSecurityNative.HeimdalNtlmException(SR.Format(SR.net_context_buffer_too_small, bufferLength, available)); + } + + Marshal.Copy(data, destination, offset, bufferLength); + return bufferLength; + } + + internal byte[] ToByteArray() + { + if (data == IntPtr.Zero || length == 0) + { + return Array.Empty(); + } + + int bufferLength = Convert.ToInt32(length); + byte[] destination = new byte[bufferLength]; + Marshal.Copy(data, destination, 0, bufferLength); + return destination; + } + + public void Dispose() + { + if (data != IntPtr.Zero) + { + Interop.NetSecurityNative.ReleaseNtlmBuffer(data, length); + data = IntPtr.Zero; + } + + length = 0; + } + } + } +} + diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmType3Message.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmType3Message.cs new file mode 100644 index 000000000000..3d12f6393448 --- /dev/null +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmType3Message.cs @@ -0,0 +1,99 @@ +// 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 System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Authentication; +using System.Security.Authentication.ExtendedProtection; +using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; +using System.Text; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class HeimdalNtlm + { + /// + /// Provides ntlm_type3 message + /// + internal sealed class NtlmType3Message : IDisposable + { + private SafeNtlmType2Handle _type2Handle; + + public NtlmType3Message(byte[] type2Data, int offset, int count) + { + Debug.Assert(type2Data != null, "type2Data cannot be null"); + Debug.Assert(offset >= 0 && offset <= type2Data.Length, "offset must be a valid value"); + Debug.Assert(count >= 0 , " count must be a valid value"); + Debug.Assert(type2Data.Length >= offset + count, " count and offset must match the given buffer"); + + int status = Interop.NetSecurityNative.HeimNtlmDecodeType2(type2Data, offset, count, out _type2Handle); + Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + } + + public byte[] GetResponse(uint flags, string username, string password, string domain, + out byte[] sessionKey) + { + Debug.Assert(username != null, "username cannot be null"); + Debug.Assert(password != null, "password cannot be null"); + Debug.Assert(domain != null, "domain cannot be null"); + MockUtils.MockLogging.PrintInfo("kapilash", "GetResponse.1"); + + // reference for NTLM response: https://msdn.microsoft.com/en-us/library/cc236700.aspx + + sessionKey = null; + Interop.NetSecurityNative.NtlmBuffer key = default(Interop.NetSecurityNative.NtlmBuffer); + Interop.NetSecurityNative.NtlmBuffer lmResponse = default(Interop.NetSecurityNative.NtlmBuffer); + Interop.NetSecurityNative.NtlmBuffer ntResponse = default(Interop.NetSecurityNative.NtlmBuffer); + Interop.NetSecurityNative.NtlmBuffer sessionKeyBuffer = default(Interop.NetSecurityNative.NtlmBuffer); + Interop.NetSecurityNative.NtlmBuffer outputData = default(Interop.NetSecurityNative.NtlmBuffer); + + try + { + int status = Interop.NetSecurityNative.HeimNtlmNtKey(password, ref key); + Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + MockUtils.MockLogging.PrintInfo("kapilash", "GetResponse.2"); + + byte[] baseSessionKey = new byte[Interop.NetSecurityNative.MD5DigestLength]; + status = Interop.NetSecurityNative.HeimNtlmCalculateResponse(true, ref key, _type2Handle, username, domain, + baseSessionKey, baseSessionKey.Length, ref lmResponse); + Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + MockUtils.MockLogging.PrintInfo("kapilash", "GetResponse.3"); + + status = Interop.NetSecurityNative.HeimNtlmCalculateResponse(false, ref key, _type2Handle, username, domain, + baseSessionKey, baseSessionKey.Length, ref ntResponse); + Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + MockUtils.MockLogging.PrintInfo("kapilash", "GetResponse.4"); + + status = Interop.NetSecurityNative.CreateType3Message(ref key, _type2Handle, username, domain, flags, + ref lmResponse, ref ntResponse, baseSessionKey,baseSessionKey.Length, ref sessionKeyBuffer, ref outputData); + Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + MockUtils.MockLogging.PrintInfo("kapilash", "GetResponse.5"); + + sessionKey = sessionKeyBuffer.ToByteArray(); + return outputData.ToByteArray(); + } + finally + { + key.Dispose(); + lmResponse.Dispose(); + ntResponse.Dispose(); + sessionKeyBuffer.Dispose(); + outputData.Dispose(); + } + } + + public void Dispose() + { + if (_type2Handle != null) + { + _type2Handle.Dispose(); + _type2Handle = null; + } + } + } + } +} diff --git a/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs b/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs index d2ad88d0b653..2acf9ec5adb4 100644 --- a/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs +++ b/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs @@ -1,5 +1,6 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// 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 System; using System.Diagnostics; @@ -12,165 +13,6 @@ namespace Microsoft.Win32.SafeHandles { - /// - /// Wrapper around a ntlm_buf* - /// - internal sealed class SafeNtlmBufferHandle : SafeHandle - { - public SafeNtlmBufferHandle() - : base(IntPtr.Zero, true) - { - } - - public byte[] ToByteArray(int length, int offset) - { - Debug.Assert(length >= 0, "negative length of buffer"); - Debug.Assert(offset < length, "invalid offset for buffer"); - byte[] target = new byte[length]; - Interop.NetSecurity.ExtractNtlmBuffer(this, target, capacity: length, offset: offset); - return target; - } - - public override bool IsInvalid - { - get { return handle == IntPtr.Zero; } - } - - protected override bool ReleaseHandle() - { - Interop.NetSecurity.HeimNtlmFreeBuf(handle); - SetHandle(IntPtr.Zero); - return true; - } - } - - /// - /// Wrapper around a session key used for signing - /// - internal sealed class SafeNtlmKeyHandle : SafeHandle - { - private uint _sequenceNumber; - private readonly bool _isSealingKey; - private readonly byte[] _digest; - private SafeEvpCipherCtxHandle _cipherContext; - - // From MS_NLMP SIGNKEY at https://msdn.microsoft.com/en-us/library/cc236711.aspx - private const string s_keyMagic = "session key to {0}-to-{1} {2} key magic constant\0"; - private const string s_client = "client"; - private const string s_server = "server"; - private const string s_signing = "signing"; - private const string s_sealing = "sealing"; - - public SafeNtlmKeyHandle(byte[] key, bool isClient, bool isSealingKey) - : base(IntPtr.Zero, true) - { - string keyMagic = string.Format(s_keyMagic, isClient ? s_client : s_server, - isClient ? s_server : s_client, isSealingKey ? s_sealing : s_signing); - - using (IncrementalHash incremental = IncrementalHash.CreateHash(HashAlgorithmName.MD5)) - { - incremental.AppendData(key); - incremental.AppendData(Encoding.UTF8.GetBytes(keyMagic)); - _digest = incremental.GetHashAndReset(); - } - - _isSealingKey = isSealingKey; - if (_isSealingKey) - { - _cipherContext = Interop.Crypto.EvpCipherCreate(Interop.Crypto.EvpRc4(), _digest, null, 1); - } - } - - public override bool IsInvalid - { - get { return true; } - } - - protected override void Dispose(bool disposing) - { - if (disposing && (null != _cipherContext)) - { - _cipherContext.Dispose(); - _cipherContext = null; - } - base.Dispose(disposing); - } - - protected override bool ReleaseHandle() - { - return true; - } - - public byte[] Sign(SafeNtlmKeyHandle sealingKey, byte[] buffer, int offset, int count) - { - Debug.Assert(!_isSealingKey, "Cannot sign with sealing key"); - Debug.Assert(offset >= 0 && offset < buffer.Length, "Cannot sign with invalid offset " + offset); - Debug.Assert((count + offset) <= buffer.Length, "Cannot sign with invalid count " + count); - - // reference for signing a message: https://msdn.microsoft.com/en-us/library/cc236702.aspx - const uint Version = 0x00000001; - const int ChecksumOffset = 4; - const int SequenceNumberOffset = 12; - const int HMacDigestLength = 8; - - byte[] output = new byte[Interop.NetSecurity.MD5DigestLength]; - Array.Clear(output, 0, output.Length); - MarshalUint(output, 0, Version); // version - MarshalUint(output, SequenceNumberOffset, _sequenceNumber); - byte[] hash; - - using (var incremental = IncrementalHash.CreateHMAC(HashAlgorithmName.MD5, _digest)) - { - incremental.AppendData(output, SequenceNumberOffset, ChecksumOffset); - incremental.AppendData(buffer, offset, count); - hash = incremental.GetHashAndReset(); - _sequenceNumber++; - } - - if (sealingKey == null) - { - Array.Copy(hash, 0, output, ChecksumOffset, HMacDigestLength); - } - else - { - byte[] cipher = sealingKey.SealOrUnseal(true, hash, 0, HMacDigestLength); - Array.Copy(cipher, 0, output, ChecksumOffset, cipher.Length); - } - - return output; - } - - public byte[] SealOrUnseal(bool seal, byte[] buffer, int offset, int count) - { - //Message Confidentiality. Reference: https://msdn.microsoft.com/en-us/library/cc236707.aspx - Debug.Assert(_isSealingKey, "Cannot seal or unseal with signing key"); - Debug.Assert(offset >= 0 && offset < buffer.Length, "Cannot sign with invalid offset " + offset); - Debug.Assert((count + offset) <= buffer.Length, "Cannot sign with invalid count "); - - unsafe - { - fixed (byte* bytePtr = buffer) - { - // Since RC4 is XOR-based, encrypt or decrypt is relative to input data - // reference: https://msdn.microsoft.com/en-us/library/cc236707.aspx - byte[] output = new byte[count]; - - Interop.Crypto.EvpCipher(_cipherContext, output, (bytePtr + offset), count); - return output; - } - } - } - - private static void MarshalUint(byte[] ptr, int offset, uint num) - { - for (int i = 0; i < 4; i++) - { - ptr[offset + i] = (byte) (num & 0xff); - num >>= 8; - } - } - } - /// /// Wrapper around a ntlm_type2* /// @@ -183,7 +25,7 @@ public override bool IsInvalid protected override bool ReleaseHandle() { - Interop.NetSecurity.HeimNtlmFreeType2(handle); + Interop.NetSecurityNative.HeimNtlmFreeType2(handle); SetHandle(IntPtr.Zero); return true; } @@ -192,80 +34,4 @@ private SafeNtlmType2Handle() : base(IntPtr.Zero, true) { } } - - /// - /// Wrapper around a ntlm_type3* - /// - internal sealed class SafeNtlmType3Handle : SafeHandle - { - private SafeNtlmType2Handle _type2Handle; - public SafeNtlmType3Handle(byte[] type2Data, int offset, int count) : base(IntPtr.Zero, true) - { - int status = Interop.NetSecurity.HeimNtlmDecodeType2(type2Data, offset, count, out _type2Handle); - Interop.NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("HeimNtlmDecodeType2 failed", status); - } - - public override bool IsInvalid - { - get { return (null != _type2Handle) && !_type2Handle.IsInvalid; } - } - - public byte[] GetResponse(uint flags, string username, string password, string domain, - out byte[] sessionKey) - { - // reference for NTLM response: https://msdn.microsoft.com/en-us/library/cc236700.aspx - sessionKey = null; - SafeNtlmBufferHandle key; - int keyLen; - int status = Interop.NetSecurity.HeimNtlmNtKey(password, out key, out keyLen); - Interop.NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("HeimNtlmKey failed", status); - - using (key) - { - byte[] baseSessionKey = new byte[Interop.NetSecurity.MD5DigestLength]; - SafeNtlmBufferHandle lmResponse; - int lmResponseLength; - - status = Interop.NetSecurity.HeimNtlmCalculateResponse(true, key, _type2Handle, username, domain, - baseSessionKey, baseSessionKey.Length, out lmResponse, out lmResponseLength); - Interop.NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("HeimNtlmCalculateResponse lm1 failed",status); - - SafeNtlmBufferHandle ntResponse; - int ntResponseLength; - status = Interop.NetSecurity.HeimNtlmCalculateResponse(false, key, _type2Handle, username, domain, - baseSessionKey, baseSessionKey.Length, out ntResponse, out ntResponseLength); - Interop.NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("HeimNtlmCalculateResponse lm2 failed", status); - - SafeNtlmBufferHandle sessionKeyHandle = null; - int sessionKeyLen; - SafeNtlmBufferHandle outputData = null; - int outputDataLen = 0; - status = Interop.NetSecurity.CreateType3Message(key, _type2Handle, username, domain, flags, lmResponse, ntResponse, baseSessionKey, - baseSessionKey.Length, out sessionKeyHandle, out sessionKeyLen, out outputData, - out outputDataLen); - Interop.NetSecurity.HeimdalNtlmException.AssertOrThrowIfError("CreateType3Message failed", status); - using (sessionKeyHandle) - using (outputData) - { - sessionKey = sessionKeyHandle.ToByteArray(sessionKeyLen,0); - return outputData.ToByteArray(outputDataLen, 0); - } - } - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - _type2Handle.Dispose(); - _type2Handle = null; - } - base.Dispose(disposing); - } - - protected override bool ReleaseHandle() - { - return true; - } - } } diff --git a/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp b/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp index 6994cbd84322..977b1112d12d 100644 --- a/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp +++ b/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp @@ -1,5 +1,6 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// 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. #include #include @@ -26,52 +27,54 @@ static_assert(PAL_NTLMSSP_NEGOTIATE_KEY_EXCH == NTLM_NEG_KEYEX, ""); const int32_t MD5_DIGEST_LENGTH = 16; -static inline int32_t NetSecurity_SetBufferLength(int32_t status, ntlm_buf** bufferHandle, int32_t* outLength) +static inline int32_t +NetSecurityNative_SetBufferLength(int32_t status, ntlm_buf* ntlmBuffer, struct PAL_NtlmBuffer* targetBuffer) { + assert(ntlmBuffer != nullptr); + assert(targetBuffer != nullptr); + if (status != 0) { - delete *bufferHandle; - *bufferHandle = nullptr; - *outLength = 0; - return status; + targetBuffer->length = 0; + targetBuffer->data = nullptr; } - *outLength = static_cast((*bufferHandle)->length); + else + { + assert(targetBuffer->length == 0 || targetBuffer->data != nullptr); + targetBuffer->length = ntlmBuffer->length; + targetBuffer->data = ntlmBuffer->data; + } + return status; } -extern "C" void NetSecurity_HeimNtlmFreeBuf(ntlm_buf* data) +extern "C" void NetSecurityNative_ReleaseNtlmBuffer(void* buffer, uint64_t length) { - assert(data != nullptr); - heim_ntlm_free_buf(data); - delete data; -} + assert(buffer != nullptr); -extern "C" void NetSecurity_ExtractNtlmBuffer(const ntlm_buf* bufferHandle, uint8_t* destination, uint32_t capacity, uint32_t offset) -{ - if (bufferHandle == nullptr) - { - return; - } - assert(destination != nullptr); - assert(bufferHandle->length <= (capacity - offset)); - memcpy(destination + UnsignedCast(offset), bufferHandle->data, bufferHandle->length); + ntlm_buf ntlmBuffer{.length = length, .data = buffer}; + heim_ntlm_free_buf(&ntlmBuffer); } -extern "C" int32_t NetSecurity_HeimNtlmEncodeType1(uint32_t flags, ntlm_buf** outBufferHandle, int32_t* outLength) +extern "C" int32_t NetSecurityNative_HeimNtlmEncodeType1(uint32_t flags, struct PAL_NtlmBuffer* outBuffer) { - assert(outBufferHandle != nullptr); + assert(outBuffer != nullptr); + ntlm_type1 type1; + ntlm_buf ntlmBuffer{.length = 0, .data = nullptr}; memset(&type1, 0, sizeof(ntlm_type1)); type1.flags = flags; - *outBufferHandle = new ntlm_buf(); - return NetSecurity_SetBufferLength(heim_ntlm_encode_type1(&type1, *outBufferHandle), outBufferHandle, outLength); + return NetSecurityNative_SetBufferLength(heim_ntlm_encode_type1(&type1, &ntlmBuffer), &ntlmBuffer, outBuffer); } -extern "C" int32_t NetSecurity_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2) +extern "C" int32_t +NetSecurityNative_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2) { assert(data != nullptr); assert(offset >= 0); + assert(count >= 0); assert(type2 != nullptr); + ntlm_buf buffer{.length = UnsignedCast(count), .data = data + offset}; *type2 = new ntlm_type2(); int32_t stat = heim_ntlm_decode_type2(&buffer, *type2); @@ -80,74 +83,81 @@ extern "C" int32_t NetSecurity_HeimNtlmDecodeType2(uint8_t* data, int32_t offset delete *type2; *type2 = nullptr; } + return stat; } -extern "C" void NetSecurity_HeimNtlmFreeType2(ntlm_type2* type2) +extern "C" void NetSecurityNative_HeimNtlmFreeType2(ntlm_type2* type2) { assert(type2 != nullptr); + heim_ntlm_free_type2(type2); delete type2; } -extern "C" int32_t NetSecurity_HeimNtlmNtKey(const char* password, ntlm_buf** outBufferHandle, int32_t* outLength) +extern "C" int32_t NetSecurityNative_HeimNtlmNtKey(const char* password, struct PAL_NtlmBuffer* outBuffer) { - assert(outBufferHandle != nullptr); - *outBufferHandle = new ntlm_buf(); - return NetSecurity_SetBufferLength(heim_ntlm_nt_key(password, *outBufferHandle), outBufferHandle, outLength); + assert(outBuffer != nullptr); + assert(password != nullptr); + + ntlm_buf ntlmBuffer{.length = 0, .data = nullptr}; + return NetSecurityNative_SetBufferLength(heim_ntlm_nt_key(password, &ntlmBuffer), &ntlmBuffer, outBuffer); } -extern "C" int32_t NetSecurity_HeimNtlmCalculateResponse(int32_t isLM, - const ntlm_buf* key, - ntlm_type2* type2, - char* username, - char* target, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, - ntlm_buf** outBufferHandle, - int32_t* outLength) +extern "C" int32_t NetSecurityNative_HeimNtlmCalculateResponse(int32_t isLM, + const struct PAL_NtlmBuffer* key, + ntlm_type2* type2, + char* username, + char* target, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + struct PAL_NtlmBuffer* outBuffer) { // reference doc: http://msdn.microsoft.com/en-us/library/cc236700.aspx - assert(baseSessionKeyLen == MD5_DIGEST_LENGTH); assert(isLM == 0 || isLM == 1); - assert(type2 != nullptr); assert(key != nullptr); - assert(outBufferHandle != nullptr); - *outBufferHandle = new ntlm_buf(); + assert(type2 != nullptr); + assert(username != nullptr); + assert(target != nullptr); + assert(baseSessionKey != nullptr); + assert(baseSessionKeyLen == MD5_DIGEST_LENGTH); + assert(outBuffer != nullptr); + + ntlm_buf ntlmBuffer{.length = 0, .data = nullptr}; if (isLM) { - return NetSecurity_SetBufferLength( + return NetSecurityNative_SetBufferLength( heim_ntlm_calculate_lm2( - key->data, key->length, username, target, type2->challenge, baseSessionKey, *outBufferHandle), - outBufferHandle, - outLength); + key->data, key->length, username, target, type2->challenge, baseSessionKey, &ntlmBuffer), + &ntlmBuffer, + outBuffer); } else { if (type2->targetinfo.length == 0) { - return NetSecurity_SetBufferLength( - heim_ntlm_calculate_ntlm1(key->data, key->length, type2->challenge, *outBufferHandle), - outBufferHandle, - outLength); + return NetSecurityNative_SetBufferLength( + heim_ntlm_calculate_ntlm1(key->data, key->length, type2->challenge, &ntlmBuffer), + &ntlmBuffer, + outBuffer); } else { - return NetSecurity_SetBufferLength(heim_ntlm_calculate_ntlm2(key->data, - key->length, - username, - target, - type2->challenge, - &type2->targetinfo, - baseSessionKey, - *outBufferHandle), - outBufferHandle, - outLength); + return NetSecurityNative_SetBufferLength(heim_ntlm_calculate_ntlm2(key->data, + key->length, + username, + target, + type2->challenge, + &type2->targetinfo, + baseSessionKey, + &ntlmBuffer), + &ntlmBuffer, + outBuffer); } } } -static uint8_t* NetSecurity_HMACDigest(uint8_t* key, int32_t keylen, void* input, size_t inputlen) +static uint8_t* NetSecurityNative_HMACDigest(uint8_t* key, int32_t keylen, void* input, size_t inputlen) { HMAC_CTX ctx; uint8_t* output = new uint8_t[16]; @@ -161,7 +171,7 @@ static uint8_t* NetSecurity_HMACDigest(uint8_t* key, int32_t keylen, void* input return output; } -static uint8_t* NetSecurity_EVPEncrypt(uint8_t* key, void* input, size_t inputlen) +static uint8_t* NetSecurityNative_EVPEncrypt(uint8_t* key, void* input, size_t inputlen) { EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init(&ctx); @@ -174,11 +184,11 @@ static uint8_t* NetSecurity_EVPEncrypt(uint8_t* key, void* input, size_t inputle return output; } -static int32_t -NetSecurity_build_ntlm2_master(uint8_t* key, int32_t keylen, ntlm_buf* blob, ntlm_buf* sessionKey, ntlm_buf* masterKey) +static int32_t NetSecurityNative_build_ntlm2_master( + uint8_t* key, int32_t keylen, ntlm_buf* blob, ntlm_buf* sessionKey, ntlm_buf* masterKey) { // reference: https://msdn.microsoft.com/en-us/library/cc236709.aspx - uint8_t* ntlmv2hash = NetSecurity_HMACDigest(key, keylen, blob->data, blob->length); + uint8_t* ntlmv2hash = NetSecurityNative_HMACDigest(key, keylen, blob->data, blob->length); int32_t status = heim_ntlm_build_ntlm1_master(ntlmv2hash, UnsignedCast(keylen), sessionKey, masterKey); if (status) { @@ -186,71 +196,81 @@ NetSecurity_build_ntlm2_master(uint8_t* key, int32_t keylen, ntlm_buf* blob, ntl return status; } - uint8_t* exportKey = NetSecurity_EVPEncrypt(ntlmv2hash, sessionKey->data, sessionKey->length); + uint8_t* exportKey = NetSecurityNative_EVPEncrypt(ntlmv2hash, sessionKey->data, sessionKey->length); delete[] ntlmv2hash; masterKey->length = sessionKey->length; masterKey->data = exportKey; return status; } -extern "C" int32_t NetSecurity_CreateType3Message(ntlm_buf* key, - ntlm_type2* type2, - const char* username, - const char* domain, - uint32_t flags, - ntlm_buf* lmResponse, - ntlm_buf* ntlmResponse, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, - ntlm_buf** outSessionHandle, - int* outSessionKeyLen, - ntlm_buf** outBufferHandle, - int32_t* outLength) +extern "C" int32_t NetSecurityNative_CreateType3Message(struct PAL_NtlmBuffer* key, + ntlm_type2* type2, + const char* username, + const char* domain, + uint32_t flags, + struct PAL_NtlmBuffer* lmResponse, + struct PAL_NtlmBuffer* ntlmResponse, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + struct PAL_NtlmBuffer* outSessionKey, + struct PAL_NtlmBuffer* outBuffer) { assert(key != nullptr); assert(type2 != nullptr); + assert(username != nullptr); + assert(domain != nullptr); assert(lmResponse != nullptr); assert(ntlmResponse != nullptr); + assert(baseSessionKey != nullptr); + assert(baseSessionKeyLen > 0); + assert(outSessionKey != nullptr); + assert(outBuffer != nullptr); + + outBuffer->length = 0; + outBuffer->data = nullptr; + outSessionKey->length = 0; + outSessionKey->data = nullptr; + static char* workstation = static_cast(calloc(1, sizeof(char))); // empty string ntlm_type3 type3; memset(&type3, 0, sizeof(ntlm_type3)); type3.username = const_cast(username); type3.targetname = const_cast(domain); - type3.lm = *lmResponse; - type3.ntlm = *ntlmResponse; + type3.lm = {.length = lmResponse->length, .data = lmResponse->data}; + type3.ntlm = {.length = ntlmResponse->length, .data = ntlmResponse->data}; type3.ws = workstation; type3.flags = flags; int32_t status = 0; - ntlm_buf masterKey = {.length = 0, .data = nullptr}; - *outSessionHandle = new ntlm_buf(); + ntlm_buf masterKey{.length = 0, .data = nullptr}; + ntlm_buf ntlmSessionKey{.length = 0, .data = nullptr}; if (type2->targetinfo.length == 0) { - status = heim_ntlm_build_ntlm1_master(key->data, key->length, *outSessionHandle, &masterKey); + status = heim_ntlm_build_ntlm1_master(key->data, key->length, &ntlmSessionKey, &masterKey); } else { // Only first 16 bytes of the NTLMv2 response should be passed assert(ntlmResponse->length >= MD5_DIGEST_LENGTH); - ntlm_buf blob = {.length = MD5_DIGEST_LENGTH, .data = ntlmResponse->data}; + ntlm_buf blob{.length = MD5_DIGEST_LENGTH, .data = ntlmResponse->data}; status = - NetSecurity_build_ntlm2_master(baseSessionKey, baseSessionKeyLen, &blob, *outSessionHandle, &masterKey); + NetSecurityNative_build_ntlm2_master(baseSessionKey, baseSessionKeyLen, &blob, &ntlmSessionKey, &masterKey); } - status = NetSecurity_SetBufferLength(status, outSessionHandle, outSessionKeyLen); + + status = NetSecurityNative_SetBufferLength(status, &ntlmSessionKey, outSessionKey); if (status != 0) { return status; } - *outBufferHandle = new ntlm_buf(); + ntlm_buf ntlmBuffer{.length = 0, .data = nullptr}; type3.sessionkey = masterKey; - status = heim_ntlm_encode_type3(&type3, *outBufferHandle); + status = heim_ntlm_encode_type3(&type3, &ntlmBuffer); if (status != 0) { - heim_ntlm_free_buf(*outSessionHandle); - delete *outSessionHandle; - *outSessionKeyLen = 0; + heim_ntlm_free_buf(&ntlmBuffer); + return status; } if (type2->targetinfo.length == 0) @@ -259,9 +279,11 @@ extern "C" int32_t NetSecurity_CreateType3Message(ntlm_buf* key, } else { - // in case of v2, masterKey.data is created by NetSecurity_build_ntlm2_master function and free_buf cannot be + // in case of v2, masterKey.data is created by NetSecurityNative_build_ntlm2_master function and free_buf cannot + // be // called. delete[] static_cast(masterKey.data); } - return NetSecurity_SetBufferLength(status, outBufferHandle, outLength); + + return NetSecurityNative_SetBufferLength(status, &ntlmBuffer, outBuffer); } diff --git a/src/Native/System.Net.Security.Native/pal_ntlmapi.h b/src/Native/System.Net.Security.Native/pal_ntlmapi.h index 30a6678ad06f..8333b1ffa95f 100644 --- a/src/Native/System.Net.Security.Native/pal_ntlmapi.h +++ b/src/Native/System.Net.Security.Native/pal_ntlmapi.h @@ -1,5 +1,6 @@ -// Copyright (c) Microsoft. All rights reserved. -// Licensed under the MIT license. See LICENSE file in the project root for full license information. +// 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. #pragma once @@ -16,66 +17,62 @@ enum NtlmFlags : int32_t PAL_NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000, }; -struct ntlm_buf; +struct PAL_NtlmBuffer +{ + uint64_t length; + void* data; +}; struct ntlm_type2; /* Shims heim_ntlm_free_buf method. */ -extern "C" void NetSecurity_HeimNtlmFreeBuf(ntlm_buf* data); - -/* -Copies data from ntlm_buffer into an array of given size, from the offset. -*/ -extern "C" void -NetSecurity_ExtractNtlmBuffer(const ntlm_buf* bufferHandle, uint8_t* destination, uint32_t capacity, uint32_t offset); +extern "C" void NetSecurityNative_ReleaseNtlmBuffer(void* buffer, uint64_t length); /* Shims heim_ntlm_encode_type1 method. */ -extern "C" int32_t NetSecurity_HeimNtlmEncodeType1(uint32_t flags, ntlm_buf** outBufferHandle, int* outLength); +extern "C" int32_t NetSecurityNative_HeimNtlmEncodeType1(uint32_t flags, struct PAL_NtlmBuffer* outBuffer); /* Shims heim_ntlm_decode_type2 method. */ -extern "C" int32_t NetSecurity_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2); +extern "C" int32_t +NetSecurityNative_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2); /* Shims heim_ntlm_free_type2 method. */ -extern "C" void NetSecurity_HeimNtlmFreeType2(ntlm_type2* type2); +extern "C" void NetSecurityNative_HeimNtlmFreeType2(ntlm_type2* type2); /* Shims heim_ntlm_nt_key method. */ -extern "C" int32_t NetSecurity_HeimNtlmNtKey(const char* password, ntlm_buf** outBufferHandle, int* outLength); +extern "C" int32_t NetSecurityNative_HeimNtlmNtKey(const char* password, struct PAL_NtlmBuffer* outBuffer); /* Shims heim_ntlm_calculate_lm2/_ntlm2 methods. */ -extern "C" int32_t NetSecurity_HeimNtlmCalculateResponse(int32_t isLM, - const ntlm_buf* key, - ntlm_type2* type2, - char* username, - char* target, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, - ntlm_buf** data, - int* outLength); +extern "C" int32_t NetSecurityNative_HeimNtlmCalculateResponse(int32_t isLM, + const struct PAL_NtlmBuffer* key, + ntlm_type2* type2, + char* username, + char* target, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + struct PAL_NtlmBuffer* data); /* Implements Type3 msg proccessing logic */ -extern "C" int32_t NetSecurity_CreateType3Message(ntlm_buf* key, - ntlm_type2* type2, - const char* username, - const char* domain, - uint32_t flags, - ntlm_buf* lmResponse, - ntlm_buf* ntlmResponse, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, - ntlm_buf** outSessionHandle, - int32_t* outSessionKeyLen, - ntlm_buf** outBufferHandle, - int32_t* outLength); +extern "C" int32_t NetSecurityNative_CreateType3Message(struct PAL_NtlmBuffer* key, + ntlm_type2* type2, + const char* username, + const char* domain, + uint32_t flags, + struct PAL_NtlmBuffer* lmResponse, + struct PAL_NtlmBuffer* ntlmResponse, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + struct PAL_NtlmBuffer* outSessionKey, + struct PAL_NtlmBuffer* outBufferHandle); From 3f1ff30572876df4678c2b1fad75cd30d01ed66a Mon Sep 17 00:00:00 2001 From: kapilash Date: Mon, 8 Feb 2016 17:13:13 +0530 Subject: [PATCH 4/8] PR comments 1. UInt64 to ulong 2. Method name changed to fit the appropriate type being marshalled 3. Reduce the allocations done by using a static byte array --- .../Interop.HeimdalNtlm.cs | 24 +++++++++---------- .../Interop.NtlmBuffer.cs | 2 +- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs index cd110e9aa3de..ee1d500997ba 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs @@ -16,22 +16,22 @@ internal static partial class Interop { internal static partial class HeimdalNtlm { - private const string ClientToServerSigningMagicKey = "session key to client-to-server signing key magic constant\0"; - private const string ServerToClientSigningMagicKey = "session key to server-to-client signing key magic constant\0"; - private const string ServerToClientSealingMagicKey = "session key to server-to-client sealing key magic constant\0"; - private const string ClientToServerSealingMagicKey = "session key to client-to-server sealing key magic constant\0"; + private static readonly byte[] ClientToServerSigningMagicKey = Encoding.UTF8.GetBytes("session key to client-to-server signing key magic constant\0"); + private static readonly byte[] ServerToClientSigningMagicKey = Encoding.UTF8.GetBytes("session key to server-to-client signing key magic constant\0"); + private static readonly byte[] ServerToClientSealingMagicKey = Encoding.UTF8.GetBytes("session key to server-to-client sealing key magic constant\0"); + private static readonly byte[] ClientToServerSealingMagicKey = Encoding.UTF8.GetBytes("session key to client-to-server sealing key magic constant\0"); internal sealed class SigningKey { private uint _sequenceNumber; private readonly byte[] _digest; - public SigningKey(byte[] key, string magicKey) + public SigningKey(byte[] key, byte[] magicKey) { using (IncrementalHash incremental = IncrementalHash.CreateHash(HashAlgorithmName.MD5)) { incremental.AppendData(key); - incremental.AppendData(Encoding.UTF8.GetBytes(magicKey)); + incremental.AppendData(magicKey); _digest = incremental.GetHashAndReset(); } } @@ -48,8 +48,8 @@ public byte[] Sign(SealingKey sealingKey, byte[] buffer, int offset, int count) const int HMacDigestLength = 8; byte[] output = new byte[Interop.NetSecurityNative.MD5DigestLength]; - MarshalUint(output, 0, Version); // version - MarshalUint(output, SequenceNumberOffset, _sequenceNumber); + MarshalUint32(output, 0, Version); // version + MarshalUint32(output, SequenceNumberOffset, _sequenceNumber); byte[] hash; using (var incremental = IncrementalHash.CreateHMAC(HashAlgorithmName.MD5, _digest)) @@ -79,7 +79,7 @@ internal sealed class SealingKey : IDisposable private readonly byte[] _digest; private SafeEvpCipherCtxHandle _cipherContext; - public SealingKey(byte[] key, string magicKey) + public SealingKey(byte[] key, byte[] magicKey) { _digest = NtlmKeyDigest(key, magicKey); _cipherContext = Interop.Crypto.EvpCipherCreate(Interop.Crypto.EvpRc4(), _digest, null, 1); @@ -117,17 +117,17 @@ public void Dispose() } } - private static byte[] NtlmKeyDigest(byte[] key, string magicKey) + private static byte[] NtlmKeyDigest(byte[] key, byte[] magicKey) { using (IncrementalHash incremental = IncrementalHash.CreateHash(HashAlgorithmName.MD5)) { incremental.AppendData(key); - incremental.AppendData(Encoding.UTF8.GetBytes(magicKey)); + incremental.AppendData(magicKey); return incremental.GetHashAndReset(); } } - private static void MarshalUint(byte[] ptr, int offset, uint num) + private static void MarshalUint32(byte[] ptr, int offset, uint num) { for (int i = 0; i < 4; i++) { diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmBuffer.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmBuffer.cs index 98d77efd0bfc..ed54506d52dd 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmBuffer.cs +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmBuffer.cs @@ -14,7 +14,7 @@ internal static partial class NetSecurityNative [StructLayout(LayoutKind.Sequential)] internal unsafe struct NtlmBuffer : IDisposable { - internal UInt64 length; + internal ulong length; internal IntPtr data; internal int Copy(byte[] destination, int offset) From a752a3f23dc1b523edccb0cb9ea8d5e463aa35cf Mon Sep 17 00:00:00 2001 From: kapilash Date: Tue, 9 Feb 2016 20:41:37 +0530 Subject: [PATCH 5/8] NtlmFlags to enum with Flags attribute --- .../Interop.HeimdalNtlm.cs | 2 -- .../Interop.Ntlm.cs | 20 +++++++++---------- .../Interop.NtlmType3Message.cs | 5 ----- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs index ee1d500997ba..7b4d4e83027f 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs @@ -101,7 +101,6 @@ public byte[] SealOrUnseal(byte[] buffer, int offset, int count) byte[] output = new byte[count]; Interop.Crypto.EvpCipher(_cipherContext, output, (bytePtr + offset), count); - MockUtils.MockLogging.PrintInfo(null, "returning from seal"); return output; } } @@ -143,7 +142,6 @@ internal static byte[] CreateNegotiateMessage(uint flags) { int status = NetSecurityNative.HeimNtlmEncodeType1(flags, ref buffer); NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); - MockUtils.MockLogging.PrintInfo("kapilash", "created negotiate message"); return buffer.ToByteArray(); } finally diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs index 24f272e65871..4690b5de5120 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs @@ -57,17 +57,17 @@ internal static extern int CreateType3Message( ref NtlmBuffer sessionKey, ref NtlmBuffer data); - internal partial class NtlmFlags + internal enum NtlmFlags { - internal const uint NTLMSSP_NEGOTIATE_UNICODE = 0x1; - internal const uint NTLMSSP_REQUEST_TARGET = 0x4; - internal const uint NTLMSSP_NEGOTIATE_SIGN = 0x10; - internal const uint NTLMSSP_NEGOTIATE_SEAL = 0x20; - internal const uint NTLMSSP_NEGOTIATE_NTLM = 0x200; - internal const uint NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x8000; - internal const uint NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x80000; - internal const uint NTLMSSP_NEGOTIATE_128 = 0x20000000; - internal const uint NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000; + NTLMSSP_NEGOTIATE_UNICODE = 0x1, + NTLMSSP_REQUEST_TARGET = 0x4, + NTLMSSP_NEGOTIATE_SIGN = 0x10, + NTLMSSP_NEGOTIATE_SEAL = 0x20, + NTLMSSP_NEGOTIATE_NTLM = 0x200, + NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x8000, + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x80000, + NTLMSSP_NEGOTIATE_128 = 0x20000000, + NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000 } } } diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmType3Message.cs b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmType3Message.cs index 3d12f6393448..b49f264abbc9 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmType3Message.cs +++ b/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmType3Message.cs @@ -40,7 +40,6 @@ public byte[] GetResponse(uint flags, string username, string password, string d Debug.Assert(username != null, "username cannot be null"); Debug.Assert(password != null, "password cannot be null"); Debug.Assert(domain != null, "domain cannot be null"); - MockUtils.MockLogging.PrintInfo("kapilash", "GetResponse.1"); // reference for NTLM response: https://msdn.microsoft.com/en-us/library/cc236700.aspx @@ -55,23 +54,19 @@ public byte[] GetResponse(uint flags, string username, string password, string d { int status = Interop.NetSecurityNative.HeimNtlmNtKey(password, ref key); Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); - MockUtils.MockLogging.PrintInfo("kapilash", "GetResponse.2"); byte[] baseSessionKey = new byte[Interop.NetSecurityNative.MD5DigestLength]; status = Interop.NetSecurityNative.HeimNtlmCalculateResponse(true, ref key, _type2Handle, username, domain, baseSessionKey, baseSessionKey.Length, ref lmResponse); Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); - MockUtils.MockLogging.PrintInfo("kapilash", "GetResponse.3"); status = Interop.NetSecurityNative.HeimNtlmCalculateResponse(false, ref key, _type2Handle, username, domain, baseSessionKey, baseSessionKey.Length, ref ntResponse); Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); - MockUtils.MockLogging.PrintInfo("kapilash", "GetResponse.4"); status = Interop.NetSecurityNative.CreateType3Message(ref key, _type2Handle, username, domain, flags, ref lmResponse, ref ntResponse, baseSessionKey,baseSessionKey.Length, ref sessionKeyBuffer, ref outputData); Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); - MockUtils.MockLogging.PrintInfo("kapilash", "GetResponse.5"); sessionKey = sessionKeyBuffer.ToByteArray(); return outputData.ToByteArray(); From 7b2cec12dadb970f17bf2b15abfd137aea1a1f0f Mon Sep 17 00:00:00 2001 From: Hemanth Kapila Date: Fri, 19 Feb 2016 15:21:14 +0530 Subject: [PATCH 6/8] Change Library to a different folder --- .../src/Interop/Linux/Interop.Libraries.cs | 12 +++ .../Interop.HeimdalNtlm.cs | 8 +- .../Interop.HeimdalNtlmException.cs | 2 +- .../Interop.Ntlm.cs | 16 +-- .../Interop.NtlmBuffer.cs | 8 +- .../Interop.NtlmType3Message.cs | 32 +++--- .../SafeHandles/NtlmSecuritySafeHandles.cs | 4 +- src/Native/CMakeLists.txt | 1 + .../System.Net.Ntlm.Native/CMakeLists.txt | 32 ++++++ .../pal_ntlmapi.cpp | 101 +++++++++--------- .../System.Net.Ntlm.Native/pal_ntlmapi.h | 77 +++++++++++++ .../System.Net.Security.Native/pal_ntlmapi.h | 78 -------------- 12 files changed, 208 insertions(+), 163 deletions(-) create mode 100644 src/Common/src/Interop/Linux/Interop.Libraries.cs rename src/Common/src/Interop/Linux/{System.Net.Security.Native => System.Net.Ntlm.Native}/Interop.HeimdalNtlm.cs (95%) rename src/Common/src/Interop/Linux/{System.Net.Security.Native => System.Net.Ntlm.Native}/Interop.HeimdalNtlmException.cs (94%) rename src/Common/src/Interop/Linux/{System.Net.Security.Native => System.Net.Ntlm.Native}/Interop.Ntlm.cs (72%) rename src/Common/src/Interop/Linux/{System.Net.Security.Native => System.Net.Ntlm.Native}/Interop.NtlmBuffer.cs (86%) rename src/Common/src/Interop/Linux/{System.Net.Security.Native => System.Net.Ntlm.Native}/Interop.NtlmType3Message.cs (64%) create mode 100644 src/Native/System.Net.Ntlm.Native/CMakeLists.txt rename src/Native/{System.Net.Security.Native => System.Net.Ntlm.Native}/pal_ntlmapi.cpp (64%) create mode 100644 src/Native/System.Net.Ntlm.Native/pal_ntlmapi.h delete mode 100644 src/Native/System.Net.Security.Native/pal_ntlmapi.h diff --git a/src/Common/src/Interop/Linux/Interop.Libraries.cs b/src/Common/src/Interop/Linux/Interop.Libraries.cs new file mode 100644 index 000000000000..dcc397f94b50 --- /dev/null +++ b/src/Common/src/Interop/Linux/Interop.Libraries.cs @@ -0,0 +1,12 @@ +// 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. + +internal static partial class Interop +{ + private static partial class Libraries + { + // Shims + internal const string NetNtlmNative = "System.Net.Ntlm.Native"; + } +} diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlm.cs similarity index 95% rename from src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs rename to src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlm.cs index 7b4d4e83027f..ce21a2fc9ff7 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlm.cs +++ b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlm.cs @@ -47,7 +47,7 @@ public byte[] Sign(SealingKey sealingKey, byte[] buffer, int offset, int count) const int SequenceNumberOffset = 12; const int HMacDigestLength = 8; - byte[] output = new byte[Interop.NetSecurityNative.MD5DigestLength]; + byte[] output = new byte[Interop.NetNtlmNative.MD5DigestLength]; MarshalUint32(output, 0, Version); // version MarshalUint32(output, SequenceNumberOffset, _sequenceNumber); byte[] hash; @@ -137,11 +137,11 @@ private static void MarshalUint32(byte[] ptr, int offset, uint num) internal static byte[] CreateNegotiateMessage(uint flags) { - NetSecurityNative.NtlmBuffer buffer = default(NetSecurityNative.NtlmBuffer); + NetNtlmNative.NtlmBuffer buffer = default(NetNtlmNative.NtlmBuffer); try { - int status = NetSecurityNative.HeimNtlmEncodeType1(flags, ref buffer); - NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + int status = NetNtlmNative.HeimNtlmEncodeType1(flags, ref buffer); + NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); return buffer.ToByteArray(); } finally diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlmException.cs b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlmException.cs similarity index 94% rename from src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlmException.cs rename to src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlmException.cs index 4722e60e0b2e..87628517a220 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.HeimdalNtlmException.cs +++ b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlmException.cs @@ -8,7 +8,7 @@ internal static partial class Interop { - internal static partial class NetSecurityNative + internal static partial class NetNtlmNative { internal sealed class HeimdalNtlmException : Exception { diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.Ntlm.cs similarity index 72% rename from src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs rename to src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.Ntlm.cs index 4690b5de5120..f35f5f69c467 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.Ntlm.cs +++ b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.Ntlm.cs @@ -11,28 +11,28 @@ internal static partial class Interop { - internal static partial class NetSecurityNative + internal static partial class NetNtlmNative { // The following constant is used in calculation of NTOWF2 // reference: https://msdn.microsoft.com/en-us/library/cc236700.aspx public const int MD5DigestLength = 16; - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_ReleaseNtlmBuffer")] + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_ReleaseNtlmBuffer")] internal static extern int ReleaseNtlmBuffer(IntPtr bufferPtr, UInt64 length); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_HeimNtlmEncodeType1")] + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_HeimNtlmEncodeType1")] internal static extern int HeimNtlmEncodeType1(uint flags, ref NtlmBuffer buffer); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_HeimNtlmDecodeType2")] + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_HeimNtlmDecodeType2")] internal static extern int HeimNtlmDecodeType2(byte[] data, int offset, int count, out SafeNtlmType2Handle type2Handle); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_HeimNtlmFreeType2")] + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_HeimNtlmFreeType2")] internal static extern int HeimNtlmFreeType2(IntPtr type2Handle); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_HeimNtlmNtKey", CharSet = CharSet.Ansi)] + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_HeimNtlmNtKey", CharSet = CharSet.Ansi)] internal static extern int HeimNtlmNtKey(string password, ref NtlmBuffer key); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_HeimNtlmCalculateResponse", CharSet = CharSet.Ansi)] + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_HeimNtlmCalculateResponse", CharSet = CharSet.Ansi)] internal static extern int HeimNtlmCalculateResponse( bool isLM, ref NtlmBuffer key, @@ -43,7 +43,7 @@ internal static extern int HeimNtlmCalculateResponse( int baseSessionKeyLen, ref NtlmBuffer answer); - [DllImport(Interop.Libraries.NetSecurityNative, EntryPoint="NetSecurityNative_CreateType3Message", CharSet = CharSet.Ansi)] + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_CreateType3Message", CharSet = CharSet.Ansi)] internal static extern int CreateType3Message( ref NtlmBuffer key, SafeNtlmType2Handle type2Handle, diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmBuffer.cs b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmBuffer.cs similarity index 86% rename from src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmBuffer.cs rename to src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmBuffer.cs index ed54506d52dd..d1ac639b26a0 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmBuffer.cs +++ b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmBuffer.cs @@ -9,7 +9,7 @@ internal static partial class Interop { - internal static partial class NetSecurityNative + internal static partial class NetNtlmNative { [StructLayout(LayoutKind.Sequential)] internal unsafe struct NtlmBuffer : IDisposable @@ -20,7 +20,7 @@ internal unsafe struct NtlmBuffer : IDisposable internal int Copy(byte[] destination, int offset) { Debug.Assert(destination != null, "target destination cannot be null"); - Debug.Assert(offset >= 0 && offset <= destination.Length, "invalid offset " + offset); + Debug.Assert(offset >= 0 && offset <= destination.Length, "offset must be valid "); if (data == IntPtr.Zero || length == 0) { @@ -31,7 +31,7 @@ internal int Copy(byte[] destination, int offset) int available = destination.Length - offset; // amount of space in the given buffer if (bufferLength > available) { - throw new NetSecurityNative.HeimdalNtlmException(SR.Format(SR.net_context_buffer_too_small, bufferLength, available)); + throw new NetNtlmNative.HeimdalNtlmException(SR.Format(SR.net_context_buffer_too_small, bufferLength, available)); } Marshal.Copy(data, destination, offset, bufferLength); @@ -55,7 +55,7 @@ public void Dispose() { if (data != IntPtr.Zero) { - Interop.NetSecurityNative.ReleaseNtlmBuffer(data, length); + Interop.NetNtlmNative.ReleaseNtlmBuffer(data, length); data = IntPtr.Zero; } diff --git a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmType3Message.cs b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmType3Message.cs similarity index 64% rename from src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmType3Message.cs rename to src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmType3Message.cs index b49f264abbc9..faaa45ccbe03 100644 --- a/src/Common/src/Interop/Linux/System.Net.Security.Native/Interop.NtlmType3Message.cs +++ b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmType3Message.cs @@ -30,8 +30,8 @@ public NtlmType3Message(byte[] type2Data, int offset, int count) Debug.Assert(count >= 0 , " count must be a valid value"); Debug.Assert(type2Data.Length >= offset + count, " count and offset must match the given buffer"); - int status = Interop.NetSecurityNative.HeimNtlmDecodeType2(type2Data, offset, count, out _type2Handle); - Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + int status = Interop.NetNtlmNative.HeimNtlmDecodeType2(type2Data, offset, count, out _type2Handle); + Interop.NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); } public byte[] GetResponse(uint flags, string username, string password, string domain, @@ -44,29 +44,29 @@ public byte[] GetResponse(uint flags, string username, string password, string d // reference for NTLM response: https://msdn.microsoft.com/en-us/library/cc236700.aspx sessionKey = null; - Interop.NetSecurityNative.NtlmBuffer key = default(Interop.NetSecurityNative.NtlmBuffer); - Interop.NetSecurityNative.NtlmBuffer lmResponse = default(Interop.NetSecurityNative.NtlmBuffer); - Interop.NetSecurityNative.NtlmBuffer ntResponse = default(Interop.NetSecurityNative.NtlmBuffer); - Interop.NetSecurityNative.NtlmBuffer sessionKeyBuffer = default(Interop.NetSecurityNative.NtlmBuffer); - Interop.NetSecurityNative.NtlmBuffer outputData = default(Interop.NetSecurityNative.NtlmBuffer); + Interop.NetNtlmNative.NtlmBuffer key = default(Interop.NetNtlmNative.NtlmBuffer); + Interop.NetNtlmNative.NtlmBuffer lmResponse = default(Interop.NetNtlmNative.NtlmBuffer); + Interop.NetNtlmNative.NtlmBuffer ntResponse = default(Interop.NetNtlmNative.NtlmBuffer); + Interop.NetNtlmNative.NtlmBuffer sessionKeyBuffer = default(Interop.NetNtlmNative.NtlmBuffer); + Interop.NetNtlmNative.NtlmBuffer outputData = default(Interop.NetNtlmNative.NtlmBuffer); try { - int status = Interop.NetSecurityNative.HeimNtlmNtKey(password, ref key); - Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + int status = Interop.NetNtlmNative.HeimNtlmNtKey(password, ref key); + Interop.NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); - byte[] baseSessionKey = new byte[Interop.NetSecurityNative.MD5DigestLength]; - status = Interop.NetSecurityNative.HeimNtlmCalculateResponse(true, ref key, _type2Handle, username, domain, + byte[] baseSessionKey = new byte[Interop.NetNtlmNative.MD5DigestLength]; + status = Interop.NetNtlmNative.HeimNtlmCalculateResponse(true, ref key, _type2Handle, username, domain, baseSessionKey, baseSessionKey.Length, ref lmResponse); - Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + Interop.NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); - status = Interop.NetSecurityNative.HeimNtlmCalculateResponse(false, ref key, _type2Handle, username, domain, + status = Interop.NetNtlmNative.HeimNtlmCalculateResponse(false, ref key, _type2Handle, username, domain, baseSessionKey, baseSessionKey.Length, ref ntResponse); - Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + Interop.NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); - status = Interop.NetSecurityNative.CreateType3Message(ref key, _type2Handle, username, domain, flags, + status = Interop.NetNtlmNative.CreateType3Message(ref key, _type2Handle, username, domain, flags, ref lmResponse, ref ntResponse, baseSessionKey,baseSessionKey.Length, ref sessionKeyBuffer, ref outputData); - Interop.NetSecurityNative.HeimdalNtlmException.ThrowIfError(status); + Interop.NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); sessionKey = sessionKeyBuffer.ToByteArray(); return outputData.ToByteArray(); diff --git a/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs b/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs index 2acf9ec5adb4..970d87c333d5 100644 --- a/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs +++ b/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs @@ -14,7 +14,7 @@ namespace Microsoft.Win32.SafeHandles { /// - /// Wrapper around a ntlm_type2* + /// Wrapper around a ntlm_type2 handle /// internal sealed class SafeNtlmType2Handle : SafeHandle { @@ -25,7 +25,7 @@ public override bool IsInvalid protected override bool ReleaseHandle() { - Interop.NetSecurityNative.HeimNtlmFreeType2(handle); + Interop.NetNtlmNative.HeimNtlmFreeType2(handle); SetHandle(IntPtr.Zero); return true; } diff --git a/src/Native/CMakeLists.txt b/src/Native/CMakeLists.txt index 78b41f4ac53d..085f0f65de77 100644 --- a/src/Native/CMakeLists.txt +++ b/src/Native/CMakeLists.txt @@ -92,3 +92,4 @@ add_subdirectory(System.Native) add_subdirectory(System.Net.Http.Native) add_subdirectory(System.Security.Cryptography.Native) add_subdirectory(System.Net.Security.Native) +add_subdirectory(System.Net.Ntlm.Native) diff --git a/src/Native/System.Net.Ntlm.Native/CMakeLists.txt b/src/Native/System.Net.Ntlm.Native/CMakeLists.txt new file mode 100644 index 000000000000..a1b5b2bfed4c --- /dev/null +++ b/src/Native/System.Net.Ntlm.Native/CMakeLists.txt @@ -0,0 +1,32 @@ +project(System.Net.Ntlm.Native) + +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +add_definitions(-DPIC=1 -Wno-padded) + +find_library(LIB_HEIMNTLM NAMES heimntlm PATHS /usr/heimdal/lib /usr/lib/x86_64-linux-gnu/heimdal/) +find_package(OpenSSL REQUIRED) + +if(LIB_HEIMNTLM STREQUAL LIB_HEIMNTLM-NOTFOUND) + message(WARNING "Cannot find libheimntlm.so. System.Net.Security.NegotiateStream will be built without NTLM support. Try installing libheimntlm (or the appropriate package for your platform) for NTLM") +else() + include_directories(/usr/heimdal/include) + include_directories(/usr/include/heimdal) + + set(NATIVENTLM_SOURCES + pal_ntlmapi.cpp + ) + + add_library(System.Net.Ntlm.Native + SHARED + ${NATIVENTLM_SOURCES} + ) + + target_link_libraries(System.Net.Ntlm.Native + ${LIB_HEIMNTLM} + ${OPENSSL_CRYPTO_LIBRARY} + ) + + install (TARGETS System.Net.Ntlm.Native DESTINATION .) +endif() + diff --git a/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp b/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.cpp similarity index 64% rename from src/Native/System.Net.Security.Native/pal_ntlmapi.cpp rename to src/Native/System.Net.Ntlm.Native/pal_ntlmapi.cpp index 977b1112d12d..9ad26d44c8a4 100644 --- a/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp +++ b/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.cpp @@ -28,7 +28,7 @@ static_assert(PAL_NTLMSSP_NEGOTIATE_KEY_EXCH == NTLM_NEG_KEYEX, ""); const int32_t MD5_DIGEST_LENGTH = 16; static inline int32_t -NetSecurityNative_SetBufferLength(int32_t status, ntlm_buf* ntlmBuffer, struct PAL_NtlmBuffer* targetBuffer) +NetNtlmNative_SetBufferLength(int32_t status, ntlm_buf* ntlmBuffer, struct PAL_NtlmBuffer* targetBuffer) { assert(ntlmBuffer != nullptr); assert(targetBuffer != nullptr); @@ -41,6 +41,7 @@ NetSecurityNative_SetBufferLength(int32_t status, ntlm_buf* ntlmBuffer, struct P else { assert(targetBuffer->length == 0 || targetBuffer->data != nullptr); + targetBuffer->length = ntlmBuffer->length; targetBuffer->data = ntlmBuffer->data; } @@ -48,7 +49,7 @@ NetSecurityNative_SetBufferLength(int32_t status, ntlm_buf* ntlmBuffer, struct P return status; } -extern "C" void NetSecurityNative_ReleaseNtlmBuffer(void* buffer, uint64_t length) +extern "C" void NetNtlmNative_ReleaseNtlmBuffer(void* buffer, uint64_t length) { assert(buffer != nullptr); @@ -56,7 +57,7 @@ extern "C" void NetSecurityNative_ReleaseNtlmBuffer(void* buffer, uint64_t lengt heim_ntlm_free_buf(&ntlmBuffer); } -extern "C" int32_t NetSecurityNative_HeimNtlmEncodeType1(uint32_t flags, struct PAL_NtlmBuffer* outBuffer) +extern "C" int32_t NetNtlmNative_HeimNtlmEncodeType1(uint32_t flags, struct PAL_NtlmBuffer* outBuffer) { assert(outBuffer != nullptr); @@ -64,11 +65,10 @@ extern "C" int32_t NetSecurityNative_HeimNtlmEncodeType1(uint32_t flags, struct ntlm_buf ntlmBuffer{.length = 0, .data = nullptr}; memset(&type1, 0, sizeof(ntlm_type1)); type1.flags = flags; - return NetSecurityNative_SetBufferLength(heim_ntlm_encode_type1(&type1, &ntlmBuffer), &ntlmBuffer, outBuffer); + return NetNtlmNative_SetBufferLength(heim_ntlm_encode_type1(&type1, &ntlmBuffer), &ntlmBuffer, outBuffer); } -extern "C" int32_t -NetSecurityNative_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2) +extern "C" int32_t NetNtlmNative_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2) { assert(data != nullptr); assert(offset >= 0); @@ -87,7 +87,7 @@ NetSecurityNative_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t cou return stat; } -extern "C" void NetSecurityNative_HeimNtlmFreeType2(ntlm_type2* type2) +extern "C" void NetNtlmNative_HeimNtlmFreeType2(ntlm_type2* type2) { assert(type2 != nullptr); @@ -95,23 +95,23 @@ extern "C" void NetSecurityNative_HeimNtlmFreeType2(ntlm_type2* type2) delete type2; } -extern "C" int32_t NetSecurityNative_HeimNtlmNtKey(const char* password, struct PAL_NtlmBuffer* outBuffer) +extern "C" int32_t NetNtlmNative_HeimNtlmNtKey(const char* password, struct PAL_NtlmBuffer* outBuffer) { assert(outBuffer != nullptr); assert(password != nullptr); ntlm_buf ntlmBuffer{.length = 0, .data = nullptr}; - return NetSecurityNative_SetBufferLength(heim_ntlm_nt_key(password, &ntlmBuffer), &ntlmBuffer, outBuffer); + return NetNtlmNative_SetBufferLength(heim_ntlm_nt_key(password, &ntlmBuffer), &ntlmBuffer, outBuffer); } -extern "C" int32_t NetSecurityNative_HeimNtlmCalculateResponse(int32_t isLM, - const struct PAL_NtlmBuffer* key, - ntlm_type2* type2, - char* username, - char* target, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, - struct PAL_NtlmBuffer* outBuffer) +extern "C" int32_t NetNtlmNative_HeimNtlmCalculateResponse(int32_t isLM, + const struct PAL_NtlmBuffer* key, + ntlm_type2* type2, + char* username, + char* target, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + struct PAL_NtlmBuffer* outBuffer) { // reference doc: http://msdn.microsoft.com/en-us/library/cc236700.aspx assert(isLM == 0 || isLM == 1); @@ -126,7 +126,7 @@ extern "C" int32_t NetSecurityNative_HeimNtlmCalculateResponse(int32_t isLM, ntlm_buf ntlmBuffer{.length = 0, .data = nullptr}; if (isLM) { - return NetSecurityNative_SetBufferLength( + return NetNtlmNative_SetBufferLength( heim_ntlm_calculate_lm2( key->data, key->length, username, target, type2->challenge, baseSessionKey, &ntlmBuffer), &ntlmBuffer, @@ -136,28 +136,28 @@ extern "C" int32_t NetSecurityNative_HeimNtlmCalculateResponse(int32_t isLM, { if (type2->targetinfo.length == 0) { - return NetSecurityNative_SetBufferLength( + return NetNtlmNative_SetBufferLength( heim_ntlm_calculate_ntlm1(key->data, key->length, type2->challenge, &ntlmBuffer), &ntlmBuffer, outBuffer); } else { - return NetSecurityNative_SetBufferLength(heim_ntlm_calculate_ntlm2(key->data, - key->length, - username, - target, - type2->challenge, - &type2->targetinfo, - baseSessionKey, - &ntlmBuffer), - &ntlmBuffer, - outBuffer); + return NetNtlmNative_SetBufferLength(heim_ntlm_calculate_ntlm2(key->data, + key->length, + username, + target, + type2->challenge, + &type2->targetinfo, + baseSessionKey, + &ntlmBuffer), + &ntlmBuffer, + outBuffer); } } } -static uint8_t* NetSecurityNative_HMACDigest(uint8_t* key, int32_t keylen, void* input, size_t inputlen) +static uint8_t* NetNtlmNative_HMACDigest(uint8_t* key, int32_t keylen, void* input, size_t inputlen) { HMAC_CTX ctx; uint8_t* output = new uint8_t[16]; @@ -171,7 +171,7 @@ static uint8_t* NetSecurityNative_HMACDigest(uint8_t* key, int32_t keylen, void* return output; } -static uint8_t* NetSecurityNative_EVPEncrypt(uint8_t* key, void* input, size_t inputlen) +static uint8_t* NetNtlmNative_EVPEncrypt(uint8_t* key, void* input, size_t inputlen) { EVP_CIPHER_CTX ctx; EVP_CIPHER_CTX_init(&ctx); @@ -184,11 +184,11 @@ static uint8_t* NetSecurityNative_EVPEncrypt(uint8_t* key, void* input, size_t i return output; } -static int32_t NetSecurityNative_build_ntlm2_master( +static int32_t NetNtlmNative_build_ntlm2_master( uint8_t* key, int32_t keylen, ntlm_buf* blob, ntlm_buf* sessionKey, ntlm_buf* masterKey) { // reference: https://msdn.microsoft.com/en-us/library/cc236709.aspx - uint8_t* ntlmv2hash = NetSecurityNative_HMACDigest(key, keylen, blob->data, blob->length); + uint8_t* ntlmv2hash = NetNtlmNative_HMACDigest(key, keylen, blob->data, blob->length); int32_t status = heim_ntlm_build_ntlm1_master(ntlmv2hash, UnsignedCast(keylen), sessionKey, masterKey); if (status) { @@ -196,24 +196,25 @@ static int32_t NetSecurityNative_build_ntlm2_master( return status; } - uint8_t* exportKey = NetSecurityNative_EVPEncrypt(ntlmv2hash, sessionKey->data, sessionKey->length); + heim_ntlm_free_buf(masterKey); + uint8_t* exportKey = NetNtlmNative_EVPEncrypt(ntlmv2hash, sessionKey->data, sessionKey->length); delete[] ntlmv2hash; masterKey->length = sessionKey->length; masterKey->data = exportKey; return status; } -extern "C" int32_t NetSecurityNative_CreateType3Message(struct PAL_NtlmBuffer* key, - ntlm_type2* type2, - const char* username, - const char* domain, - uint32_t flags, - struct PAL_NtlmBuffer* lmResponse, - struct PAL_NtlmBuffer* ntlmResponse, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, - struct PAL_NtlmBuffer* outSessionKey, - struct PAL_NtlmBuffer* outBuffer) +extern "C" int32_t NetNtlmNative_CreateType3Message(struct PAL_NtlmBuffer* key, + ntlm_type2* type2, + const char* username, + const char* domain, + uint32_t flags, + struct PAL_NtlmBuffer* lmResponse, + struct PAL_NtlmBuffer* ntlmResponse, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + struct PAL_NtlmBuffer* outSessionKey, + struct PAL_NtlmBuffer* outBuffer) { assert(key != nullptr); assert(type2 != nullptr); @@ -244,6 +245,7 @@ extern "C" int32_t NetSecurityNative_CreateType3Message(struct PAL_NtlmBuffer* k int32_t status = 0; ntlm_buf masterKey{.length = 0, .data = nullptr}; ntlm_buf ntlmSessionKey{.length = 0, .data = nullptr}; + ntlm_buf ntlmBuffer{.length = 0, .data = nullptr}; if (type2->targetinfo.length == 0) { @@ -255,16 +257,15 @@ extern "C" int32_t NetSecurityNative_CreateType3Message(struct PAL_NtlmBuffer* k assert(ntlmResponse->length >= MD5_DIGEST_LENGTH); ntlm_buf blob{.length = MD5_DIGEST_LENGTH, .data = ntlmResponse->data}; status = - NetSecurityNative_build_ntlm2_master(baseSessionKey, baseSessionKeyLen, &blob, &ntlmSessionKey, &masterKey); + NetNtlmNative_build_ntlm2_master(baseSessionKey, baseSessionKeyLen, &blob, &ntlmSessionKey, &masterKey); } - status = NetSecurityNative_SetBufferLength(status, &ntlmSessionKey, outSessionKey); + status = NetNtlmNative_SetBufferLength(status, &ntlmSessionKey, outSessionKey); if (status != 0) { return status; } - ntlm_buf ntlmBuffer{.length = 0, .data = nullptr}; type3.sessionkey = masterKey; status = heim_ntlm_encode_type3(&type3, &ntlmBuffer); if (status != 0) @@ -279,11 +280,11 @@ extern "C" int32_t NetSecurityNative_CreateType3Message(struct PAL_NtlmBuffer* k } else { - // in case of v2, masterKey.data is created by NetSecurityNative_build_ntlm2_master function and free_buf cannot + // in case of v2, masterKey.data is created by NetNtlmNative_build_ntlm2_master function and free_buf cannot // be // called. delete[] static_cast(masterKey.data); } - return NetSecurityNative_SetBufferLength(status, &ntlmBuffer, outBuffer); + return NetNtlmNative_SetBufferLength(status, &ntlmBuffer, outBuffer); } diff --git a/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.h b/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.h new file mode 100644 index 000000000000..5c0ed0555f38 --- /dev/null +++ b/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.h @@ -0,0 +1,77 @@ +// 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. + +#pragma once + +enum NtlmFlags : int32_t +{ + PAL_NTLMSSP_NEGOTIATE_UNICODE = 0x1, + PAL_NTLMSSP_REQUEST_TARGET = 0x4, + PAL_NTLMSSP_NEGOTIATE_SIGN = 0x10, + PAL_NTLMSSP_NEGOTIATE_SEAL = 0x20, + PAL_NTLMSSP_NEGOTIATE_NTLM = 0x200, + PAL_NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x8000, + PAL_NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x80000, + PAL_NTLMSSP_NEGOTIATE_128 = 0x20000000, + PAL_NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000, +}; + +struct PAL_NtlmBuffer +{ + uint64_t length; + void* data; +}; +struct ntlm_type2; + +/* +Shims heim_ntlm_free_buf method. +*/ +extern "C" void NetNtlmNative_ReleaseNtlmBuffer(void* buffer, uint64_t length); + +/* +Shims heim_ntlm_encode_type1 method. +*/ +extern "C" int32_t NetNtlmNative_HeimNtlmEncodeType1(uint32_t flags, struct PAL_NtlmBuffer* outBuffer); + +/* +Shims heim_ntlm_decode_type2 method. +*/ +extern "C" int32_t NetNtlmNative_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2); + +/* +Shims heim_ntlm_free_type2 method. +*/ +extern "C" void NetNtlmNative_HeimNtlmFreeType2(ntlm_type2* type2); + +/* +Shims heim_ntlm_nt_key method. +*/ +extern "C" int32_t NetNtlmNative_HeimNtlmNtKey(const char* password, struct PAL_NtlmBuffer* outBuffer); + +/* +Shims heim_ntlm_calculate_lm2/_ntlm2 methods. +*/ +extern "C" int32_t NetNtlmNative_HeimNtlmCalculateResponse(int32_t isLM, + const struct PAL_NtlmBuffer* key, + ntlm_type2* type2, + char* username, + char* target, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + struct PAL_NtlmBuffer* data); + +/* +Implements Type3 msg proccessing logic +*/ +extern "C" int32_t NetNtlmNative_CreateType3Message(struct PAL_NtlmBuffer* key, + ntlm_type2* type2, + const char* username, + const char* domain, + uint32_t flags, + struct PAL_NtlmBuffer* lmResponse, + struct PAL_NtlmBuffer* ntlmResponse, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + struct PAL_NtlmBuffer* outSessionKey, + struct PAL_NtlmBuffer* outBufferHandle); diff --git a/src/Native/System.Net.Security.Native/pal_ntlmapi.h b/src/Native/System.Net.Security.Native/pal_ntlmapi.h deleted file mode 100644 index 8333b1ffa95f..000000000000 --- a/src/Native/System.Net.Security.Native/pal_ntlmapi.h +++ /dev/null @@ -1,78 +0,0 @@ -// 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. - -#pragma once - -enum NtlmFlags : int32_t -{ - PAL_NTLMSSP_NEGOTIATE_UNICODE = 0x1, - PAL_NTLMSSP_REQUEST_TARGET = 0x4, - PAL_NTLMSSP_NEGOTIATE_SIGN = 0x10, - PAL_NTLMSSP_NEGOTIATE_SEAL = 0x20, - PAL_NTLMSSP_NEGOTIATE_NTLM = 0x200, - PAL_NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x8000, - PAL_NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x80000, - PAL_NTLMSSP_NEGOTIATE_128 = 0x20000000, - PAL_NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000, -}; - -struct PAL_NtlmBuffer -{ - uint64_t length; - void* data; -}; -struct ntlm_type2; - -/* -Shims heim_ntlm_free_buf method. -*/ -extern "C" void NetSecurityNative_ReleaseNtlmBuffer(void* buffer, uint64_t length); - -/* -Shims heim_ntlm_encode_type1 method. -*/ -extern "C" int32_t NetSecurityNative_HeimNtlmEncodeType1(uint32_t flags, struct PAL_NtlmBuffer* outBuffer); - -/* -Shims heim_ntlm_decode_type2 method. -*/ -extern "C" int32_t -NetSecurityNative_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2); - -/* -Shims heim_ntlm_free_type2 method. -*/ -extern "C" void NetSecurityNative_HeimNtlmFreeType2(ntlm_type2* type2); - -/* -Shims heim_ntlm_nt_key method. -*/ -extern "C" int32_t NetSecurityNative_HeimNtlmNtKey(const char* password, struct PAL_NtlmBuffer* outBuffer); - -/* -Shims heim_ntlm_calculate_lm2/_ntlm2 methods. -*/ -extern "C" int32_t NetSecurityNative_HeimNtlmCalculateResponse(int32_t isLM, - const struct PAL_NtlmBuffer* key, - ntlm_type2* type2, - char* username, - char* target, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, - struct PAL_NtlmBuffer* data); - -/* -Implements Type3 msg proccessing logic -*/ -extern "C" int32_t NetSecurityNative_CreateType3Message(struct PAL_NtlmBuffer* key, - ntlm_type2* type2, - const char* username, - const char* domain, - uint32_t flags, - struct PAL_NtlmBuffer* lmResponse, - struct PAL_NtlmBuffer* ntlmResponse, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, - struct PAL_NtlmBuffer* outSessionKey, - struct PAL_NtlmBuffer* outBufferHandle); From 7b2b35888de36ac4493456fd0597e59b73b9473a Mon Sep 17 00:00:00 2001 From: Vijay Kota Date: Tue, 16 Feb 2016 18:45:52 +0530 Subject: [PATCH 7/8] Reduce the number of Native calls Renamed HeimdalNtlm to Ntlm Renamed HeimdalNtlmException to NtlmException Naming convention in the shim layer is NetNativeNtlm_ --- .../Interop.HeimdalNtlm.cs | 171 -------------- .../Interop.NetNtlmNative.cs | 55 +++++ .../System.Net.Ntlm.Native/Interop.Ntlm.cs | 208 +++++++++++++----- .../Interop.NtlmBuffer.cs | 2 +- ...mException.cs => Interop.NtlmException.cs} | 10 +- .../Interop.NtlmType3Message.cs | 32 +-- .../SafeHandles/NtlmSecuritySafeHandles.cs | 2 +- .../System.Net.Ntlm.Native/pal_ntlmapi.cpp | 82 ++++--- .../System.Net.Ntlm.Native/pal_ntlmapi.h | 35 +-- .../src/Resources/Strings.resx | 2 + 10 files changed, 282 insertions(+), 317 deletions(-) delete mode 100644 src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlm.cs create mode 100644 src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NetNtlmNative.cs rename src/Common/src/Interop/Linux/System.Net.Ntlm.Native/{Interop.HeimdalNtlmException.cs => Interop.NtlmException.cs} (67%) diff --git a/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlm.cs b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlm.cs deleted file mode 100644 index ce21a2fc9ff7..000000000000 --- a/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlm.cs +++ /dev/null @@ -1,171 +0,0 @@ -// 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 System; -using System.Diagnostics; -using System.Runtime.InteropServices; -using System.Security.Authentication; -using System.Security.Authentication.ExtendedProtection; -using System.Security.Cryptography; -using System.Security.Cryptography.X509Certificates; -using System.Text; -using Microsoft.Win32.SafeHandles; - -internal static partial class Interop -{ - internal static partial class HeimdalNtlm - { - private static readonly byte[] ClientToServerSigningMagicKey = Encoding.UTF8.GetBytes("session key to client-to-server signing key magic constant\0"); - private static readonly byte[] ServerToClientSigningMagicKey = Encoding.UTF8.GetBytes("session key to server-to-client signing key magic constant\0"); - private static readonly byte[] ServerToClientSealingMagicKey = Encoding.UTF8.GetBytes("session key to server-to-client sealing key magic constant\0"); - private static readonly byte[] ClientToServerSealingMagicKey = Encoding.UTF8.GetBytes("session key to client-to-server sealing key magic constant\0"); - - internal sealed class SigningKey - { - private uint _sequenceNumber; - private readonly byte[] _digest; - - public SigningKey(byte[] key, byte[] magicKey) - { - using (IncrementalHash incremental = IncrementalHash.CreateHash(HashAlgorithmName.MD5)) - { - incremental.AppendData(key); - incremental.AppendData(magicKey); - _digest = incremental.GetHashAndReset(); - } - } - - public byte[] Sign(SealingKey sealingKey, byte[] buffer, int offset, int count) - { - Debug.Assert(offset >= 0 && offset <= buffer.Length, "cannot sign with invalid offset"); - Debug.Assert(count <= buffer.Length - offset, "cannot sign with invalid count"); - - // reference for signing a message: https://msdn.microsoft.com/en-us/library/cc236702.aspx - const uint Version = 0x00000001; - const int ChecksumOffset = 4; - const int SequenceNumberOffset = 12; - const int HMacDigestLength = 8; - - byte[] output = new byte[Interop.NetNtlmNative.MD5DigestLength]; - MarshalUint32(output, 0, Version); // version - MarshalUint32(output, SequenceNumberOffset, _sequenceNumber); - byte[] hash; - - using (var incremental = IncrementalHash.CreateHMAC(HashAlgorithmName.MD5, _digest)) - { - incremental.AppendData(output, SequenceNumberOffset, ChecksumOffset); - incremental.AppendData(buffer, offset, count); - hash = incremental.GetHashAndReset(); - _sequenceNumber++; - } - - if (sealingKey == null) - { - Array.Copy(hash, 0, output, ChecksumOffset, HMacDigestLength); - } - else - { - byte[] cipher = sealingKey.SealOrUnseal(hash, 0, HMacDigestLength); - Array.Copy(cipher, 0, output, ChecksumOffset, cipher.Length); - } - - return output; - } - } - - internal sealed class SealingKey : IDisposable - { - private readonly byte[] _digest; - private SafeEvpCipherCtxHandle _cipherContext; - - public SealingKey(byte[] key, byte[] magicKey) - { - _digest = NtlmKeyDigest(key, magicKey); - _cipherContext = Interop.Crypto.EvpCipherCreate(Interop.Crypto.EvpRc4(), _digest, null, 1); - } - - public byte[] SealOrUnseal(byte[] buffer, int offset, int count) - { - // Message Confidentiality. Reference: https://msdn.microsoft.com/en-us/library/cc236707.aspx - Debug.Assert(offset >= 0 && offset <= buffer.Length, "Cannot sign with invalid offset " + offset); - Debug.Assert(count >= 0, "cannot sign with invalid count"); - Debug.Assert(count <= (buffer.Length - offset), "Cannot sign with invalid count "); - - unsafe - { - fixed (byte *bytePtr = buffer) - { - // Since RC4 is XOR-based, encrypt or decrypt is relative to input data - // reference: https://msdn.microsoft.com/en-us/library/cc236707.aspx - byte[] output = new byte[count]; - - Interop.Crypto.EvpCipher(_cipherContext, output, (bytePtr + offset), count); - return output; - } - } - } - - public void Dispose() - { - if (_cipherContext != null) - { - _cipherContext.Dispose(); - _cipherContext = null; - } - } - } - - private static byte[] NtlmKeyDigest(byte[] key, byte[] magicKey) - { - using (IncrementalHash incremental = IncrementalHash.CreateHash(HashAlgorithmName.MD5)) - { - incremental.AppendData(key); - incremental.AppendData(magicKey); - return incremental.GetHashAndReset(); - } - } - - private static void MarshalUint32(byte[] ptr, int offset, uint num) - { - for (int i = 0; i < 4; i++) - { - ptr[offset + i] = (byte) (num & 0xff); - num >>= 8; - } - } - - internal static byte[] CreateNegotiateMessage(uint flags) - { - NetNtlmNative.NtlmBuffer buffer = default(NetNtlmNative.NtlmBuffer); - try - { - int status = NetNtlmNative.HeimNtlmEncodeType1(flags, ref buffer); - NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); - return buffer.ToByteArray(); - } - finally - { - buffer.Dispose(); - } - } - - internal static byte[] CreateAuthenticateMessage(uint flags, string username, string password, string domain, - byte[] type2Data, int offset, int count, out byte[] sessionKey) - { - using (NtlmType3Message challengeMessage = new NtlmType3Message(type2Data, offset, count)) - { - return challengeMessage.GetResponse(flags, username, password, domain, out sessionKey); - } - } - - internal static void CreateKeys(byte[] sessionKey, out SigningKey serverSignKey, out SealingKey serverSealKey, out SigningKey clientSignKey, out SealingKey clientSealKey) - { - serverSignKey = new SigningKey(sessionKey, ServerToClientSigningMagicKey); - serverSealKey = new SealingKey(sessionKey, ServerToClientSealingMagicKey); - clientSignKey = new SigningKey(sessionKey, ClientToServerSigningMagicKey); - clientSealKey = new SealingKey(sessionKey, ClientToServerSealingMagicKey); - } - } -} - diff --git a/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NetNtlmNative.cs b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NetNtlmNative.cs new file mode 100644 index 000000000000..8a66555ae464 --- /dev/null +++ b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NetNtlmNative.cs @@ -0,0 +1,55 @@ +// 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 System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Text; +using Microsoft.Win32.SafeHandles; + +internal static partial class Interop +{ + internal static partial class NetNtlmNative + { + // The following constant is used in calculation of NTOWF2 + // reference: https://msdn.microsoft.com/en-us/library/cc236700.aspx + public const int MD5DigestLength = 16; + + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_ReleaseNtlmBuffer")] + internal static extern int ReleaseNtlmBuffer(IntPtr bufferPtr, UInt64 length); + + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_NtlmEncodeType1")] + internal static extern int NtlmEncodeType1(uint flags, ref NtlmBuffer buffer); + + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_NtlmDecodeType2")] + internal static extern int NtlmDecodeType2(byte[] data, int offset, int count, out SafeNtlmType2Handle type2Handle); + + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_NtlmFreeType2")] + internal static extern int NtlmFreeType2(IntPtr type2Handle); + + [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_CreateType3Message", CharSet = CharSet.Ansi)] + internal static extern int CreateType3Message( + string password, + SafeNtlmType2Handle type2Handle, + string username, + string domain, + uint flags, + ref NtlmBuffer sessionKey, + ref NtlmBuffer data); + + internal enum NtlmFlags + { + NTLMSSP_NEGOTIATE_UNICODE = 0x1, + NTLMSSP_REQUEST_TARGET = 0x4, + NTLMSSP_NEGOTIATE_SIGN = 0x10, + NTLMSSP_NEGOTIATE_SEAL = 0x20, + NTLMSSP_NEGOTIATE_NTLM = 0x200, + NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x8000, + NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x80000, + NTLMSSP_NEGOTIATE_128 = 0x20000000, + NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000 + } + } +} diff --git a/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.Ntlm.cs b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.Ntlm.cs index f35f5f69c467..8330cf7bb622 100644 --- a/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.Ntlm.cs +++ b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.Ntlm.cs @@ -5,69 +5,167 @@ using System; using System.Diagnostics; using System.Runtime.InteropServices; +using System.Security.Authentication; +using System.Security.Authentication.ExtendedProtection; using System.Security.Cryptography; +using System.Security.Cryptography.X509Certificates; using System.Text; using Microsoft.Win32.SafeHandles; internal static partial class Interop { - internal static partial class NetNtlmNative + internal static partial class Ntlm { - // The following constant is used in calculation of NTOWF2 - // reference: https://msdn.microsoft.com/en-us/library/cc236700.aspx - public const int MD5DigestLength = 16; - - [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_ReleaseNtlmBuffer")] - internal static extern int ReleaseNtlmBuffer(IntPtr bufferPtr, UInt64 length); - - [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_HeimNtlmEncodeType1")] - internal static extern int HeimNtlmEncodeType1(uint flags, ref NtlmBuffer buffer); - - [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_HeimNtlmDecodeType2")] - internal static extern int HeimNtlmDecodeType2(byte[] data, int offset, int count, out SafeNtlmType2Handle type2Handle); - - [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_HeimNtlmFreeType2")] - internal static extern int HeimNtlmFreeType2(IntPtr type2Handle); - - [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_HeimNtlmNtKey", CharSet = CharSet.Ansi)] - internal static extern int HeimNtlmNtKey(string password, ref NtlmBuffer key); - - [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_HeimNtlmCalculateResponse", CharSet = CharSet.Ansi)] - internal static extern int HeimNtlmCalculateResponse( - bool isLM, - ref NtlmBuffer key, - SafeNtlmType2Handle type2Handle, - string username, - string target, - byte[] baseSessionKey, - int baseSessionKeyLen, - ref NtlmBuffer answer); - - [DllImport(Interop.Libraries.NetNtlmNative, EntryPoint="NetNtlmNative_CreateType3Message", CharSet = CharSet.Ansi)] - internal static extern int CreateType3Message( - ref NtlmBuffer key, - SafeNtlmType2Handle type2Handle, - string username, - string domain, - uint flags, - ref NtlmBuffer lmResponse, - ref NtlmBuffer ntlmResponse, - byte [] baseSessionKey, - int baseSessionKeyLen, - ref NtlmBuffer sessionKey, - ref NtlmBuffer data); - - internal enum NtlmFlags + private static readonly byte[] ClientToServerSigningMagicKey = Encoding.UTF8.GetBytes("session key to client-to-server signing key magic constant\0"); + private static readonly byte[] ServerToClientSigningMagicKey = Encoding.UTF8.GetBytes("session key to server-to-client signing key magic constant\0"); + private static readonly byte[] ServerToClientSealingMagicKey = Encoding.UTF8.GetBytes("session key to server-to-client sealing key magic constant\0"); + private static readonly byte[] ClientToServerSealingMagicKey = Encoding.UTF8.GetBytes("session key to client-to-server sealing key magic constant\0"); + + internal sealed class SigningKey + { + private uint _sequenceNumber; + private readonly byte[] _digest; + + public SigningKey(byte[] key, byte[] magicKey) + { + using (IncrementalHash incremental = IncrementalHash.CreateHash(HashAlgorithmName.MD5)) + { + incremental.AppendData(key); + incremental.AppendData(magicKey); + _digest = incremental.GetHashAndReset(); + } + } + + public byte[] Sign(SealingKey sealingKey, byte[] buffer, int offset, int count) + { + Debug.Assert(offset >= 0 && offset <= buffer.Length, "cannot sign with invalid offset"); + Debug.Assert(count <= buffer.Length - offset, "cannot sign with invalid count"); + + // reference for signing a message: https://msdn.microsoft.com/en-us/library/cc236702.aspx + const uint Version = 0x00000001; + const int ChecksumOffset = 4; + const int SequenceNumberOffset = 12; + const int HMacDigestLength = 8; + + byte[] output = new byte[Interop.NetNtlmNative.MD5DigestLength]; + MarshalUint32(output, 0, Version); // version + MarshalUint32(output, SequenceNumberOffset, _sequenceNumber); + byte[] hash; + + using (var incremental = IncrementalHash.CreateHMAC(HashAlgorithmName.MD5, _digest)) + { + incremental.AppendData(output, SequenceNumberOffset, ChecksumOffset); + incremental.AppendData(buffer, offset, count); + hash = incremental.GetHashAndReset(); + _sequenceNumber++; + } + + if (sealingKey == null) + { + Array.Copy(hash, 0, output, ChecksumOffset, HMacDigestLength); + } + else + { + byte[] cipher = sealingKey.SealOrUnseal(hash, 0, HMacDigestLength); + Array.Copy(cipher, 0, output, ChecksumOffset, cipher.Length); + } + + return output; + } + } + + internal sealed class SealingKey : IDisposable + { + private readonly byte[] _digest; + private SafeEvpCipherCtxHandle _cipherContext; + + public SealingKey(byte[] key, byte[] magicKey) + { + _digest = NtlmKeyDigest(key, magicKey); + _cipherContext = Interop.Crypto.EvpCipherCreate(Interop.Crypto.EvpRc4(), _digest, null, 1); + } + + public byte[] SealOrUnseal(byte[] buffer, int offset, int count) + { + // Message Confidentiality. Reference: https://msdn.microsoft.com/en-us/library/cc236707.aspx + Debug.Assert(offset >= 0 && offset <= buffer.Length, "Cannot sign with invalid offset " + offset); + Debug.Assert(count >= 0, "cannot sign with invalid count"); + Debug.Assert(count <= (buffer.Length - offset), "Cannot sign with invalid count "); + + unsafe + { + fixed (byte *bytePtr = buffer) + { + // Since RC4 is XOR-based, encrypt or decrypt is relative to input data + // reference: https://msdn.microsoft.com/en-us/library/cc236707.aspx + byte[] output = new byte[count]; + + Interop.Crypto.EvpCipher(_cipherContext, output, (bytePtr + offset), count); + return output; + } + } + } + + public void Dispose() + { + if (_cipherContext != null) + { + _cipherContext.Dispose(); + _cipherContext = null; + } + } + } + + private static byte[] NtlmKeyDigest(byte[] key, byte[] magicKey) + { + using (IncrementalHash incremental = IncrementalHash.CreateHash(HashAlgorithmName.MD5)) + { + incremental.AppendData(key); + incremental.AppendData(magicKey); + return incremental.GetHashAndReset(); + } + } + + private static void MarshalUint32(byte[] ptr, int offset, uint num) + { + for (int i = 0; i < 4; i++) + { + ptr[offset + i] = (byte) (num & 0xff); + num >>= 8; + } + } + + internal static byte[] CreateNegotiateMessage(uint flags) + { + NetNtlmNative.NtlmBuffer buffer = default(NetNtlmNative.NtlmBuffer); + try + { + int status = NetNtlmNative.NtlmEncodeType1(flags, ref buffer); + NetNtlmNative.NtlmException.ThrowIfError(status); + return buffer.ToByteArray(); + } + finally + { + buffer.Dispose(); + } + } + + internal static byte[] CreateAuthenticateMessage(uint flags, string username, string password, string domain, + byte[] type2Data, int offset, int count, out byte[] sessionKey) { - NTLMSSP_NEGOTIATE_UNICODE = 0x1, - NTLMSSP_REQUEST_TARGET = 0x4, - NTLMSSP_NEGOTIATE_SIGN = 0x10, - NTLMSSP_NEGOTIATE_SEAL = 0x20, - NTLMSSP_NEGOTIATE_NTLM = 0x200, - NTLMSSP_NEGOTIATE_ALWAYS_SIGN = 0x8000, - NTLMSSP_NEGOTIATE_EXTENDED_SESSIONSECURITY = 0x80000, - NTLMSSP_NEGOTIATE_128 = 0x20000000, - NTLMSSP_NEGOTIATE_KEY_EXCH = 0x40000000 + using (NtlmType3Message challengeMessage = new NtlmType3Message(type2Data, offset, count)) + { + return challengeMessage.GetResponse(flags, username, password, domain, out sessionKey); + } + } + + internal static void CreateKeys(byte[] sessionKey, out SigningKey serverSignKey, out SealingKey serverSealKey, out SigningKey clientSignKey, out SealingKey clientSealKey) + { + serverSignKey = new SigningKey(sessionKey, ServerToClientSigningMagicKey); + serverSealKey = new SealingKey(sessionKey, ServerToClientSealingMagicKey); + clientSignKey = new SigningKey(sessionKey, ClientToServerSigningMagicKey); + clientSealKey = new SealingKey(sessionKey, ClientToServerSealingMagicKey); } } } + diff --git a/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmBuffer.cs b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmBuffer.cs index d1ac639b26a0..4133fddd0dd9 100644 --- a/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmBuffer.cs +++ b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmBuffer.cs @@ -31,7 +31,7 @@ internal int Copy(byte[] destination, int offset) int available = destination.Length - offset; // amount of space in the given buffer if (bufferLength > available) { - throw new NetNtlmNative.HeimdalNtlmException(SR.Format(SR.net_context_buffer_too_small, bufferLength, available)); + throw new NetNtlmNative.NtlmException(SR.Format(SR.net_context_buffer_too_small, bufferLength, available)); } Marshal.Copy(data, destination, offset, bufferLength); diff --git a/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlmException.cs b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmException.cs similarity index 67% rename from src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlmException.cs rename to src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmException.cs index 87628517a220..ef9995a0724c 100644 --- a/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.HeimdalNtlmException.cs +++ b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmException.cs @@ -10,14 +10,14 @@ internal static partial class Interop { internal static partial class NetNtlmNative { - internal sealed class HeimdalNtlmException : Exception + internal sealed class NtlmException : Exception { - public HeimdalNtlmException(string message) : base(message) + public NtlmException(string message) : base(message) { } - public HeimdalNtlmException(int error) - : base(SR.Format(SR.net_generic_heimntlm_operation_failed, error)) + public NtlmException(int error) + : base(SR.Format(SR.net_generic_ntlm_operation_failed, error)) { HResult = error; } @@ -26,7 +26,7 @@ public static void ThrowIfError(int error) { if (error != 0) { - var ex = new HeimdalNtlmException(error); + var ex = new NtlmException(error); throw ex; } } diff --git a/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmType3Message.cs b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmType3Message.cs index faaa45ccbe03..d66da3d515ec 100644 --- a/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmType3Message.cs +++ b/src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.NtlmType3Message.cs @@ -14,7 +14,7 @@ internal static partial class Interop { - internal static partial class HeimdalNtlm + internal static partial class Ntlm { /// /// Provides ntlm_type3 message @@ -30,8 +30,8 @@ public NtlmType3Message(byte[] type2Data, int offset, int count) Debug.Assert(count >= 0 , " count must be a valid value"); Debug.Assert(type2Data.Length >= offset + count, " count and offset must match the given buffer"); - int status = Interop.NetNtlmNative.HeimNtlmDecodeType2(type2Data, offset, count, out _type2Handle); - Interop.NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); + int status = Interop.NetNtlmNative.NtlmDecodeType2(type2Data, offset, count, out _type2Handle); + Interop.NetNtlmNative.NtlmException.ThrowIfError(status); } public byte[] GetResponse(uint flags, string username, string password, string domain, @@ -41,41 +41,21 @@ public byte[] GetResponse(uint flags, string username, string password, string d Debug.Assert(password != null, "password cannot be null"); Debug.Assert(domain != null, "domain cannot be null"); - // reference for NTLM response: https://msdn.microsoft.com/en-us/library/cc236700.aspx - sessionKey = null; - Interop.NetNtlmNative.NtlmBuffer key = default(Interop.NetNtlmNative.NtlmBuffer); - Interop.NetNtlmNative.NtlmBuffer lmResponse = default(Interop.NetNtlmNative.NtlmBuffer); - Interop.NetNtlmNative.NtlmBuffer ntResponse = default(Interop.NetNtlmNative.NtlmBuffer); Interop.NetNtlmNative.NtlmBuffer sessionKeyBuffer = default(Interop.NetNtlmNative.NtlmBuffer); Interop.NetNtlmNative.NtlmBuffer outputData = default(Interop.NetNtlmNative.NtlmBuffer); try { - int status = Interop.NetNtlmNative.HeimNtlmNtKey(password, ref key); - Interop.NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); - - byte[] baseSessionKey = new byte[Interop.NetNtlmNative.MD5DigestLength]; - status = Interop.NetNtlmNative.HeimNtlmCalculateResponse(true, ref key, _type2Handle, username, domain, - baseSessionKey, baseSessionKey.Length, ref lmResponse); - Interop.NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); - - status = Interop.NetNtlmNative.HeimNtlmCalculateResponse(false, ref key, _type2Handle, username, domain, - baseSessionKey, baseSessionKey.Length, ref ntResponse); - Interop.NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); - - status = Interop.NetNtlmNative.CreateType3Message(ref key, _type2Handle, username, domain, flags, - ref lmResponse, ref ntResponse, baseSessionKey,baseSessionKey.Length, ref sessionKeyBuffer, ref outputData); - Interop.NetNtlmNative.HeimdalNtlmException.ThrowIfError(status); + int status = Interop.NetNtlmNative.CreateType3Message(password, _type2Handle, username, domain, flags, + ref sessionKeyBuffer, ref outputData); + Interop.NetNtlmNative.NtlmException.ThrowIfError(status); sessionKey = sessionKeyBuffer.ToByteArray(); return outputData.ToByteArray(); } finally { - key.Dispose(); - lmResponse.Dispose(); - ntResponse.Dispose(); sessionKeyBuffer.Dispose(); outputData.Dispose(); } diff --git a/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs b/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs index 970d87c333d5..7c51b708132b 100644 --- a/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs +++ b/src/Common/src/Microsoft/Win32/SafeHandles/NtlmSecuritySafeHandles.cs @@ -25,7 +25,7 @@ public override bool IsInvalid protected override bool ReleaseHandle() { - Interop.NetNtlmNative.HeimNtlmFreeType2(handle); + Interop.NetNtlmNative.NtlmFreeType2(handle); SetHandle(IntPtr.Zero); return true; } diff --git a/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.cpp b/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.cpp index 9ad26d44c8a4..fd4a634c9213 100644 --- a/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.cpp +++ b/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.cpp @@ -57,7 +57,7 @@ extern "C" void NetNtlmNative_ReleaseNtlmBuffer(void* buffer, uint64_t length) heim_ntlm_free_buf(&ntlmBuffer); } -extern "C" int32_t NetNtlmNative_HeimNtlmEncodeType1(uint32_t flags, struct PAL_NtlmBuffer* outBuffer) +extern "C" int32_t NetNtlmNative_NtlmEncodeType1(uint32_t flags, struct PAL_NtlmBuffer* outBuffer) { assert(outBuffer != nullptr); @@ -68,7 +68,7 @@ extern "C" int32_t NetNtlmNative_HeimNtlmEncodeType1(uint32_t flags, struct PAL_ return NetNtlmNative_SetBufferLength(heim_ntlm_encode_type1(&type1, &ntlmBuffer), &ntlmBuffer, outBuffer); } -extern "C" int32_t NetNtlmNative_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2) +extern "C" int32_t NetNtlmNative_NtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2) { assert(data != nullptr); assert(offset >= 0); @@ -87,7 +87,7 @@ extern "C" int32_t NetNtlmNative_HeimNtlmDecodeType2(uint8_t* data, int32_t offs return stat; } -extern "C" void NetNtlmNative_HeimNtlmFreeType2(ntlm_type2* type2) +extern "C" void NetNtlmNative_NtlmFreeType2(ntlm_type2* type2) { assert(type2 != nullptr); @@ -95,7 +95,7 @@ extern "C" void NetNtlmNative_HeimNtlmFreeType2(ntlm_type2* type2) delete type2; } -extern "C" int32_t NetNtlmNative_HeimNtlmNtKey(const char* password, struct PAL_NtlmBuffer* outBuffer) +static int32_t NetNtlmNative_NtlmNtKey(const char* password, struct PAL_NtlmBuffer* outBuffer) { assert(outBuffer != nullptr); assert(password != nullptr); @@ -104,14 +104,14 @@ extern "C" int32_t NetNtlmNative_HeimNtlmNtKey(const char* password, struct PAL_ return NetNtlmNative_SetBufferLength(heim_ntlm_nt_key(password, &ntlmBuffer), &ntlmBuffer, outBuffer); } -extern "C" int32_t NetNtlmNative_HeimNtlmCalculateResponse(int32_t isLM, - const struct PAL_NtlmBuffer* key, - ntlm_type2* type2, - char* username, - char* target, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, - struct PAL_NtlmBuffer* outBuffer) +static int32_t NetNtlmNative_NtlmCalculateResponse(int32_t isLM, + const struct PAL_NtlmBuffer* key, + ntlm_type2* type2, + char* username, + char* target, + uint8_t* baseSessionKey, + int32_t baseSessionKeyLen, + struct PAL_NtlmBuffer* outBuffer) { // reference doc: http://msdn.microsoft.com/en-us/library/cc236700.aspx assert(isLM == 0 || isLM == 1); @@ -204,26 +204,17 @@ static int32_t NetNtlmNative_build_ntlm2_master( return status; } -extern "C" int32_t NetNtlmNative_CreateType3Message(struct PAL_NtlmBuffer* key, +extern "C" int32_t NetNtlmNative_CreateType3Message(const char* password, ntlm_type2* type2, - const char* username, - const char* domain, + char* username, + char* domain, uint32_t flags, - struct PAL_NtlmBuffer* lmResponse, - struct PAL_NtlmBuffer* ntlmResponse, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, struct PAL_NtlmBuffer* outSessionKey, struct PAL_NtlmBuffer* outBuffer) { - assert(key != nullptr); assert(type2 != nullptr); assert(username != nullptr); assert(domain != nullptr); - assert(lmResponse != nullptr); - assert(ntlmResponse != nullptr); - assert(baseSessionKey != nullptr); - assert(baseSessionKeyLen > 0); assert(outSessionKey != nullptr); assert(outBuffer != nullptr); @@ -232,42 +223,73 @@ extern "C" int32_t NetNtlmNative_CreateType3Message(struct PAL_NtlmBuffer* key, outSessionKey->length = 0; outSessionKey->data = nullptr; + struct PAL_NtlmBuffer key, lmResponse, ntlmResponse; + int32_t status = NetNtlmNative_NtlmNtKey(password, &key); + if (status) + { + return status; + } + + // reference doc: http://msdn.microsoft.com/en-us/library/cc236700.aspx + uint8_t baseSessionKey[MD5_DIGEST_LENGTH]; + int32_t baseSessionKeyLen = static_cast(sizeof(baseSessionKey)); + status = NetNtlmNative_NtlmCalculateResponse( + true, &key, type2, username, domain, baseSessionKey, baseSessionKeyLen, &lmResponse); + if (status) + { + NetNtlmNative_ReleaseNtlmBuffer(key.data, key.length); + return status; + } + + status = NetNtlmNative_NtlmCalculateResponse( + false, &key, type2, username, domain, baseSessionKey, baseSessionKeyLen, &ntlmResponse); + if (status) + { + NetNtlmNative_ReleaseNtlmBuffer(key.data, key.length); + NetNtlmNative_ReleaseNtlmBuffer(lmResponse.data, lmResponse.length); + return status; + } + static char* workstation = static_cast(calloc(1, sizeof(char))); // empty string ntlm_type3 type3; memset(&type3, 0, sizeof(ntlm_type3)); type3.username = const_cast(username); type3.targetname = const_cast(domain); - type3.lm = {.length = lmResponse->length, .data = lmResponse->data}; - type3.ntlm = {.length = ntlmResponse->length, .data = ntlmResponse->data}; + type3.lm = {.length = lmResponse.length, .data = lmResponse.data}; + type3.ntlm = {.length = ntlmResponse.length, .data = ntlmResponse.data}; type3.ws = workstation; type3.flags = flags; - int32_t status = 0; ntlm_buf masterKey{.length = 0, .data = nullptr}; ntlm_buf ntlmSessionKey{.length = 0, .data = nullptr}; ntlm_buf ntlmBuffer{.length = 0, .data = nullptr}; if (type2->targetinfo.length == 0) { - status = heim_ntlm_build_ntlm1_master(key->data, key->length, &ntlmSessionKey, &masterKey); + status = heim_ntlm_build_ntlm1_master(key.data, key.length, &ntlmSessionKey, &masterKey); } else { // Only first 16 bytes of the NTLMv2 response should be passed - assert(ntlmResponse->length >= MD5_DIGEST_LENGTH); - ntlm_buf blob{.length = MD5_DIGEST_LENGTH, .data = ntlmResponse->data}; + assert(ntlmResponse.length >= MD5_DIGEST_LENGTH); + ntlm_buf blob{.length = MD5_DIGEST_LENGTH, .data = ntlmResponse.data}; status = NetNtlmNative_build_ntlm2_master(baseSessionKey, baseSessionKeyLen, &blob, &ntlmSessionKey, &masterKey); } + NetNtlmNative_ReleaseNtlmBuffer(key.data, key.length); status = NetNtlmNative_SetBufferLength(status, &ntlmSessionKey, outSessionKey); if (status != 0) { + NetNtlmNative_ReleaseNtlmBuffer(lmResponse.data, lmResponse.length); + NetNtlmNative_ReleaseNtlmBuffer(ntlmResponse.data, ntlmResponse.length); return status; } type3.sessionkey = masterKey; status = heim_ntlm_encode_type3(&type3, &ntlmBuffer); + NetNtlmNative_ReleaseNtlmBuffer(lmResponse.data, lmResponse.length); + NetNtlmNative_ReleaseNtlmBuffer(ntlmResponse.data, ntlmResponse.length); if (status != 0) { heim_ntlm_free_buf(&ntlmBuffer); diff --git a/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.h b/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.h index 5c0ed0555f38..b57bdd46f0e0 100644 --- a/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.h +++ b/src/Native/System.Net.Ntlm.Native/pal_ntlmapi.h @@ -32,46 +32,25 @@ extern "C" void NetNtlmNative_ReleaseNtlmBuffer(void* buffer, uint64_t length); /* Shims heim_ntlm_encode_type1 method. */ -extern "C" int32_t NetNtlmNative_HeimNtlmEncodeType1(uint32_t flags, struct PAL_NtlmBuffer* outBuffer); +extern "C" int32_t NetNtlmNative_NtlmEncodeType1(uint32_t flags, struct PAL_NtlmBuffer* outBuffer); /* Shims heim_ntlm_decode_type2 method. */ -extern "C" int32_t NetNtlmNative_HeimNtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2); +extern "C" int32_t NetNtlmNative_NtlmDecodeType2(uint8_t* data, int32_t offset, int32_t count, ntlm_type2** type2); /* Shims heim_ntlm_free_type2 method. */ -extern "C" void NetNtlmNative_HeimNtlmFreeType2(ntlm_type2* type2); - -/* -Shims heim_ntlm_nt_key method. -*/ -extern "C" int32_t NetNtlmNative_HeimNtlmNtKey(const char* password, struct PAL_NtlmBuffer* outBuffer); - -/* -Shims heim_ntlm_calculate_lm2/_ntlm2 methods. -*/ -extern "C" int32_t NetNtlmNative_HeimNtlmCalculateResponse(int32_t isLM, - const struct PAL_NtlmBuffer* key, - ntlm_type2* type2, - char* username, - char* target, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, - struct PAL_NtlmBuffer* data); +extern "C" void NetNtlmNative_NtlmFreeType2(ntlm_type2* type2); /* Implements Type3 msg proccessing logic */ -extern "C" int32_t NetNtlmNative_CreateType3Message(struct PAL_NtlmBuffer* key, +extern "C" int32_t NetNtlmNative_CreateType3Message(const char* password, ntlm_type2* type2, - const char* username, - const char* domain, + char* username, + char* domain, uint32_t flags, - struct PAL_NtlmBuffer* lmResponse, - struct PAL_NtlmBuffer* ntlmResponse, - uint8_t* baseSessionKey, - int32_t baseSessionKeyLen, struct PAL_NtlmBuffer* outSessionKey, - struct PAL_NtlmBuffer* outBufferHandle); + struct PAL_NtlmBuffer* outBuffer); diff --git a/src/System.Net.Security/src/Resources/Strings.resx b/src/System.Net.Security/src/Resources/Strings.resx index 9486f39f697a..c9cc12078fca 100644 --- a/src/System.Net.Security/src/Resources/Strings.resx +++ b/src/System.Net.Security/src/Resources/Strings.resx @@ -443,5 +443,7 @@ Heimntlm operation failed with status: {0}) + + NTLM operation failed with status: {0}) From b50c518b91e226ec90de164b73a2fcd9fd5df36c Mon Sep 17 00:00:00 2001 From: kapilash Date: Tue, 23 Feb 2016 16:32:46 +0530 Subject: [PATCH 8/8] fix resource file --- src/System.Net.Security/src/Resources/Strings.resx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/System.Net.Security/src/Resources/Strings.resx b/src/System.Net.Security/src/Resources/Strings.resx index c9cc12078fca..066943f48b26 100644 --- a/src/System.Net.Security/src/Resources/Strings.resx +++ b/src/System.Net.Security/src/Resources/Strings.resx @@ -441,8 +441,6 @@ Server implementation is not supported - - Heimntlm operation failed with status: {0}) NTLM operation failed with status: {0})