Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/Common/src/Interop/Linux/Interop.Libraries.cs
Original file line number Diff line number Diff line change
@@ -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";
}
}
Original file line number Diff line number Diff line change
@@ -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
}
}
}
171 changes: 171 additions & 0 deletions src/Common/src/Interop/Linux/System.Net.Ntlm.Native/Interop.Ntlm.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
// 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 Ntlm
{
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)
{
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);
}
}
}

Original file line number Diff line number Diff line change
@@ -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 NetNtlmNative
{
[StructLayout(LayoutKind.Sequential)]
internal unsafe struct NtlmBuffer : IDisposable
{
internal ulong 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, "offset must be valid ");

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 NetNtlmNative.NtlmException(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<byte>();
}

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.NetNtlmNative.ReleaseNtlmBuffer(data, length);
data = IntPtr.Zero;
}

length = 0;
}
}
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// 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;

internal static partial class Interop
{
internal static partial class NetNtlmNative
{
internal sealed class NtlmException : Exception
{
public NtlmException(string message) : base(message)
{
}

public NtlmException(int error)
: base(SR.Format(SR.net_generic_ntlm_operation_failed, error))
{
HResult = error;
}

public static void ThrowIfError(int error)
{
if (error != 0)
{
var ex = new NtlmException(error);
throw ex;
}
}
}
}
}

Loading