diff --git a/src/Common/src/Interop/Unix/Interop.Libraries.cs b/src/Common/src/Interop/Unix/Interop.Libraries.cs
index 8e5de4df736b..fc0ca0bc982a 100644
--- a/src/Common/src/Interop/Unix/Interop.Libraries.cs
+++ b/src/Common/src/Interop/Unix/Interop.Libraries.cs
@@ -8,6 +8,7 @@ private static partial class Libraries
// Shims
internal const string SystemNative = "System.Native";
internal const string HttpNative = "System.Net.Http.Native";
+ internal const string SecurityNative = "System.Net.Security.Native";
internal const string CryptoNative = "System.Security.Cryptography.Native";
internal const string GlobalizationNative = "System.Globalization.Native";
internal const string CompressionNative = "System.IO.Compression.Native";
diff --git a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Gss.cs b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Gss.cs
new file mode 100644
index 000000000000..20fb2155aea6
--- /dev/null
+++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Gss.cs
@@ -0,0 +1,357 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+using size_t = System.IntPtr;
+
+internal static partial class Interop
+{
+ internal static partial class libgssapi
+ {
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssReleaseBuffer(
+ out Status minorStatus,
+ ref gss_buffer_desc buffer);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssDisplayStatus(
+ out Status minorStatus,
+ Status statusValue,
+ bool isGssMechCode,
+ SafeGssBufferHandle statusString);
+
+ [DllImport(Interop.Libraries.SecurityNative, CharSet = CharSet.Ansi)]
+ internal static extern Status GssImportNtUserName(
+ out Status minorStatus,
+ string inputName,
+ out SafeGssNameHandle outputName);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssReleaseName(
+ out Status minorStatus,
+ ref IntPtr inputName);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssAcquireCredSpNego(
+ out Status minorStatus,
+ SafeGssNameHandle desiredName,
+ bool isInitiate,
+ out SafeGssCredHandle outputCredHandle);
+
+ [DllImport(Interop.Libraries.SecurityNative, CharSet = CharSet.Ansi)]
+ internal static extern Status GssAcquireCredWithPasswordSpNego(
+ out Status minorStatus,
+ SafeGssNameHandle desiredName,
+ string password,
+ bool isInitiate,
+ out SafeGssCredHandle outputCredHandle);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssReleaseCred(
+ out Status minorStatus,
+ ref IntPtr credHandle);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssInitSecContextSpNego(
+ out Status minorStatus,
+ SafeGssCredHandle initiatorCredHandle,
+ ref SafeGssContextHandle contextHandle,
+ SafeGssNameHandle targetName,
+ uint reqFlags,
+ SafeGssBufferHandle inputToken,
+ SafeGssBufferHandle outputToken,
+ out uint retFlags);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssAcceptSecContext(
+ out Status minorStatus,
+ ref SafeGssContextHandle contextHandle,
+ SafeGssCredHandle initiatorCredHandle,
+ SafeGssBufferHandle inputToken,
+ SafeGssBufferHandle outputToken,
+ out uint retFlags);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssDeleteSecContext(
+ out Status minorStatus,
+ ref IntPtr contextHandle);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssWrap(
+ out Status minorStatus,
+ SafeGssContextHandle contextHandle,
+ bool isEncrypt,
+ SafeGssBufferHandle inputMessageBuffer,
+ SafeGssBufferHandle outputMessageBuffer);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssUnwrap(
+ out Status minorStatus,
+ SafeGssContextHandle contextHandle,
+ SafeGssBufferHandle inputMessageBuffer,
+ SafeGssBufferHandle outputMessageBuffer);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssInquireSourceName(
+ out Status minorStatus,
+ SafeGssContextHandle contextHandle,
+ out SafeGssNameHandle srcName);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern Status GssDisplayName(
+ out Status minorStatus,
+ SafeGssNameHandle inputName,
+ SafeGssBufferHandle outputNameBuffer);
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct gss_buffer_desc
+ {
+ internal size_t length;
+ internal IntPtr value;
+ }
+
+ internal enum Status : uint
+ {
+ GSS_S_COMPLETE = 0,
+ GSS_S_CONTINUE_NEEDED = 1
+ }
+
+ [FlagsAttribute]
+ internal enum GssFlags : uint
+ {
+ GSS_C_DELEG_FLAG = 1,
+ GSS_C_MUTUAL_FLAG = 2,
+ GSS_C_REPLAY_FLAG = 4,
+ GSS_C_SEQUENCE_FLAG = 8,
+ GSS_C_CONF_FLAG = 16,
+ GSS_C_INTEG_FLAG = 32,
+ GSS_C_ANON_FLAG = 64,
+ GSS_C_PROT_READY_FLAG = 128,
+ GSS_C_TRANS_FLAG = 256,
+ GSS_C_DCE_STYLE = 4096,
+ GSS_C_IDENTIFY_FLAG = 8192,
+ GSS_C_EXTENDED_ERROR_FLAG = 16384,
+ GSS_C_DELEG_POLICY_FLAG = 32768
+ }
+ }
+}
+
+namespace Microsoft.Win32.SafeHandles
+{
+ ///
+ /// Wrapper around a gss_buffer_desc*
+ ///
+ internal sealed class SafeGssBufferHandle : SafeHandle
+ {
+ private GCHandle _gch;
+ private GCHandle _arrayGcHandle = new GCHandle();
+
+ // Return the buffer size
+ public int Length
+ {
+ get
+ {
+ if (IsInvalid)
+ {
+ return 0;
+ }
+ return (int)((Interop.libgssapi.gss_buffer_desc)_gch.Target).length;
+ }
+ }
+
+ // Return a pointer to where data resides
+ public IntPtr Value
+ {
+ get
+ {
+ if (IsInvalid)
+ {
+ return IntPtr.Zero;
+ }
+ return ((Interop.libgssapi.gss_buffer_desc)_gch.Target).value;
+ }
+ }
+
+ public SafeGssBufferHandle()
+ : this(0, IntPtr.Zero)
+ {
+ }
+
+ public SafeGssBufferHandle(byte[] data)
+ : this(data, 0, (data == null) ? 0 : data.Length)
+ {
+ }
+
+ public SafeGssBufferHandle(byte[] data, int offset, int count)
+ : this(count, IntPtr.Zero)
+ {
+ if (data == null) return;
+ _arrayGcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
+ IntPtr address = new IntPtr(_arrayGcHandle.AddrOfPinnedObject().ToInt64() + offset);
+ Marshal.WriteIntPtr(handle, (int)Marshal.OffsetOf("value"), address);
+ }
+
+ private SafeGssBufferHandle(int length, IntPtr ptrValue)
+ : base(IntPtr.Zero, true)
+ {
+ Interop.libgssapi.gss_buffer_desc buffer = new Interop.libgssapi.gss_buffer_desc
+ {
+ length = (size_t)length,
+ value = ptrValue,
+ };
+
+ _gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
+ handle = _gch.AddrOfPinnedObject();
+ }
+
+ public override bool IsInvalid
+ {
+ get { return handle == IntPtr.Zero; }
+ }
+
+ // Note that _value should never be freed directly. For input
+ // buffer, it is owned by the caller and for output buffer,
+ // it is owned by libgssapi
+ protected override bool ReleaseHandle()
+ {
+ Interop.libgssapi.gss_buffer_desc buffer = (Interop.libgssapi.gss_buffer_desc)_gch.Target;
+ if (buffer.value != IntPtr.Zero)
+ {
+ if (_arrayGcHandle.IsAllocated)
+ {
+ _arrayGcHandle.Free();
+ }
+ else
+ {
+ Interop.libgssapi.Status minorStatus;
+ Interop.libgssapi.Status status = Interop.libgssapi.GssReleaseBuffer(out minorStatus, ref buffer);
+ Interop.libgssapi.GssApiException.AssertOrThrowIfError("GssReleaseBuffer failed", status, minorStatus);
+ }
+ }
+ _gch.Free();
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+ }
+
+ ///
+ /// Wrapper around a gss_name_t_desc*
+ ///
+ internal sealed class SafeGssNameHandle : SafeHandle
+ {
+ public static SafeGssNameHandle Create(string name)
+ {
+ Debug.Assert(!String.IsNullOrEmpty(name), "Invalid name passed to SafeGssNameHandle create");
+ SafeGssNameHandle retHandle;
+ Interop.libgssapi.Status minorStatus;
+ Interop.libgssapi.Status status = Interop.libgssapi.GssImportNtUserName(out minorStatus, name, out retHandle);
+ if (status != Interop.libgssapi.Status.GSS_S_COMPLETE)
+ {
+ throw Interop.libgssapi.GssApiException.Create(status, minorStatus);
+ }
+
+ return retHandle;
+ }
+
+ public override bool IsInvalid
+ {
+ get { return handle == IntPtr.Zero; }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.libgssapi.Status minorStatus;
+ Interop.libgssapi.Status status = Interop.libgssapi.GssReleaseName(out minorStatus, ref handle);
+ Interop.libgssapi.GssApiException.AssertOrThrowIfError("GssReleaseName failed", status, minorStatus);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+
+ private SafeGssNameHandle()
+ : base(IntPtr.Zero, true)
+ {
+ }
+ }
+
+ ///
+ /// Wrapper around a gss_cred_id_t_desc_struct*
+ ///
+ internal class SafeGssCredHandle : SafeHandle
+ {
+ public static SafeGssCredHandle Create(string username, string password, string domain)
+ {
+ SafeGssCredHandle retHandle = null;
+
+ // Empty username is OK if Kerberos ticket was already obtained
+ if (!String.IsNullOrEmpty(username))
+ {
+ using (SafeGssNameHandle userHandle = SafeGssNameHandle.Create(username))
+ {
+ Interop.libgssapi.Status status;
+ Interop.libgssapi.Status minorStatus;
+ if (String.IsNullOrEmpty(password))
+ {
+ status = Interop.libgssapi.GssAcquireCredSpNego(out minorStatus, userHandle, true, out retHandle);
+ }
+ else
+ {
+ status = Interop.libgssapi.GssAcquireCredWithPasswordSpNego(out minorStatus, userHandle, password, true, out retHandle);
+ }
+
+ if (status != Interop.libgssapi.Status.GSS_S_COMPLETE)
+ {
+ throw Interop.libgssapi.GssApiException.Create(status, minorStatus);
+ }
+ }
+ }
+
+ return retHandle;
+ }
+
+ protected SafeGssCredHandle()
+ : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid
+ {
+ get { return handle == IntPtr.Zero; }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.libgssapi.Status minorStatus;
+ Interop.libgssapi.Status status = Interop.libgssapi.GssReleaseCred(out minorStatus, ref handle);
+ Interop.libgssapi.GssApiException.AssertOrThrowIfError("GssReleaseCred failed", status, minorStatus);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+ }
+
+ internal sealed class SafeGssContextHandle : SafeHandle
+ {
+ public SafeGssContextHandle()
+ : base(IntPtr.Zero, true)
+ {
+ }
+
+ public override bool IsInvalid
+ {
+ get { return handle == IntPtr.Zero; }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.libgssapi.Status minorStatus;
+ Interop.libgssapi.Status status = Interop.libgssapi.GssDeleteSecContext(out minorStatus, ref handle);
+ Interop.libgssapi.GssApiException.AssertOrThrowIfError("GssDeleteSecContext failed", status, minorStatus);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+ }
+}
+
diff --git a/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Ntlm.cs b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Ntlm.cs
new file mode 100644
index 000000000000..e7244d37cf4e
--- /dev/null
+++ b/src/Common/src/Interop/Unix/System.Net.Security.Native/Interop.Ntlm.cs
@@ -0,0 +1,558 @@
+// 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;
+using size_t = System.IntPtr;
+
+internal static partial class Interop
+{
+ internal static partial class libheimntlm
+ {
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern int HeimNtlmFreeBuf(ntlm_buf data);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern int HeimNtlmEncodeType1(uint flags, SafeNtlmBufferHandle data);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern int HeimNtlmDecodeType2(SafeNtlmBufferHandle data, SafeNtlmType2Handle type2);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern int HeimNtlmFreeType2(IntPtr type2);
+
+ [DllImport(Interop.Libraries.SecurityNative, CharSet = CharSet.Ansi)]
+ internal static extern unsafe int HeimNtlmNtKey(string password, SafeNtlmBufferHandle key);
+
+ [DllImport(Interop.Libraries.SecurityNative, CharSet = CharSet.Ansi)]
+ internal static extern unsafe int HeimNtlmCalculateLm2(
+ IntPtr key,
+ size_t len,
+ string username,
+ string target,
+ SafeNtlmType2Handle type2,
+ byte[] ntlmv2,
+ SafeNtlmBufferHandle answer);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern unsafe int HeimNtlmCalculateNtlm1(
+ IntPtr key,
+ size_t len,
+ SafeNtlmType2Handle type2,
+ SafeNtlmBufferHandle answer,
+ string username,
+ string target,
+ byte[] ntlmv2
+ );
+
+
+ [DllImport(Interop.Libraries.SecurityNative, CharSet = CharSet.Ansi)]
+ internal static extern unsafe int HeimNtlmCalculateNtlm2(
+ IntPtr key,
+ size_t len,
+ string username,
+ string target,
+ byte* serverchallenge,
+ SafeNtlmBufferHandle infotarget,
+ byte [] ntlmv2,
+ SafeNtlmBufferHandle answer);
+
+ //[DllImport(Interop.Libraries.SecurityNative)]
+ //internal static extern int HeimNtlmEncodeType3(ref ntlm_type3 type3, SafeNtlmBufferHandle data,
+ // ref size_t mic_offset);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern unsafe int HeimNtlmBuildNtlm1Master(
+ byte* key,
+ size_t len,
+ SafeNtlmBufferHandle session,
+ SafeNtlmBufferHandle master);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern unsafe void HeimNtlmBuildNtlm2Master(
+ byte* key,
+ size_t len,
+ SafeNtlmBufferHandle blob,
+ SafeNtlmBufferHandle session,
+ out SafeNtlmBufferHandle master);
+
+ [DllImport(Interop.Libraries.SecurityNative)]
+ internal static extern unsafe void ProcessType3Message(
+ IntPtr username,
+ IntPtr domian,
+ uint flags,
+ SafeNtlmBufferHandle lm,
+ SafeNtlmBufferHandle ntlm,
+ SafeNtlmType2Handle type2Handle,
+ IntPtr key,
+ size_t len,
+ SafeNtlmBufferHandle ntResponse,
+ SafeNtlmBufferHandle session,
+ out SafeNtlmBufferHandle master,
+ byte [] baseSessionKey,
+ uint baseSeesionKeyLen,
+ SafeNtlmBufferHandle outputData
+ );
+
+ [DllImport(Interop.Libraries.CryptoNative)]
+ internal static extern IntPtr EvpMdCtxDestroy(SafeEvpMdCtxHandle ctx);
+
+ [DllImport(Interop.Libraries.CryptoNative)]
+ private static extern void HmacDestroy(SafeHmacCtxHandle ctx);
+
+ [DllImport(Interop.Libraries.CryptoNative)]
+ private static unsafe extern void RC4SetKey(ref RC4_KEY key, int len, byte[] data);
+
+ [DllImport(Interop.Libraries.CryptoNative)]
+ private static unsafe extern void Rc4(ref RC4_KEY key, ulong len, byte* indata, byte* outdata);
+
+ [DllImport(Interop.Libraries.CryptoNative)]
+ public static extern IntPtr EvpRc4();
+
+ [DllImport(Interop.Libraries.CryptoNative)]
+ private static extern int EvpCipherDestroy(SafeEvpCipherCtxHandle ctx);
+
+ [DllImport(Interop.Libraries.CryptoNative)]
+ public static unsafe extern int EvpCipher(ref SafeEvpCipherCtxHandle ctx, byte[] output, byte* input, int inl);
+
+ 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;
+ }
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct ntlm_buf
+ {
+ internal size_t length;
+ internal IntPtr data;
+ }
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal unsafe struct RC4_KEY
+ {
+ public int x;
+ public int y;
+ public fixed int data[256];
+ }
+
+
+ internal static unsafe byte[] EVPDigest(SafeNtlmBufferHandle key, int keylen, byte[] input, int inputlen, out uint outputlen)
+ {
+ byte[] output = new byte[Interop.Crypto.EVP_MAX_MD_SIZE];
+ outputlen = 0;
+ SafeEvpMdCtxHandle ctx = Interop.Crypto.EvpMdCtxCreate(Crypto.EvpMd5());
+ try
+ {
+ Interop.Crypto.EvpDigestUpdate(ctx, (byte*)key.Value.ToPointer(), keylen);
+ fixed (byte* inPtr = input)
+ {
+ Interop.Crypto.EvpDigestUpdate(ctx, inPtr, inputlen);
+ }
+ fixed (byte* outPtr = output)
+ {
+ Interop.Crypto.EvpDigestFinalEx(ctx, outPtr, ref outputlen);
+ }
+ }
+ finally
+ {
+ EvpMdCtxDestroy(ctx);
+ }
+ return output;
+ }
+
+ internal static unsafe byte[] EVPEncryptOrDecrypt(int x, bool encrypt, byte[] key, int keylen, byte* input, int inputlen)
+ {
+ if (x > 0)
+ {
+ SafeEvpCipherCtxHandle ctx = Interop.Crypto.EvpCipherCreate(EvpRc4(), key, null, encrypt? 1: 0);
+ try
+ {
+ byte[] output = new byte[inputlen];
+ EvpCipher(ref ctx, output, input, output.Length);
+ return output;
+ }
+ finally
+ {
+ EVPFreeContext(ctx);
+ }
+ }
+ else
+ {
+ byte[] output = new byte[inputlen];
+ RC4_KEY k = new RC4_KEY();
+ RC4SetKey(ref k, keylen, key);
+ fixed (byte* outPtr = output)
+ {
+ Rc4(ref k, (ulong)inputlen, input, outPtr);
+ }
+ return output;
+ }
+ }
+
+ internal static void EVPFreeContext(SafeEvpCipherCtxHandle ctx)
+ {
+ EvpCipherDestroy(ctx);
+ }
+
+ internal static unsafe byte[] EVPEncryptOrDecrypt(SafeEvpCipherCtxHandle ctx, byte* input, int inputlen)
+ {
+ byte[] output = new byte[inputlen];
+
+ EvpCipher(ref ctx, output, input, output.Length);
+ return output;
+ }
+
+ internal static unsafe byte[] HMACDigest(byte* key, int keylen, byte* input, int inputlen, byte* prefix, int prefixlen)
+ {
+
+ SafeHmacCtxHandle ctx = null;
+ byte[] output = new byte[Interop.Crypto.EVP_MAX_MD_SIZE];
+ try
+ {
+ ctx = Interop.Crypto.HmacCreate(key, keylen, Crypto.EvpMd5());
+ if (prefixlen > 0)
+ {
+ Crypto.HmacUpdate(ctx, prefix, prefixlen);
+ }
+ Crypto.HmacUpdate(ctx, input, inputlen);
+ fixed (byte* hashPtr = output)
+ {
+ int hashLength = 0;
+ Crypto.HmacFinal(ctx, hashPtr, ref hashLength);
+ }
+ }
+ finally
+ {
+ HmacDestroy(ctx);
+ }
+ return output;
+ }
+ }
+
+}
+
+namespace Microsoft.Win32.SafeHandles
+{
+ ///
+ /// Wrapper around a ntlm_buf*
+ ///
+ internal sealed class SafeNtlmBufferHandle : SafeHandle
+ {
+ private readonly bool _isOutputBuffer;
+ private readonly GCHandle _gch;
+ private readonly GCHandle _arrayGcHandle = new GCHandle();
+
+ // Return the buffer size
+ public size_t Length
+ {
+ get
+ {
+ if (IsInvalid)
+ {
+ return (size_t) 0;
+ }
+ return ((Interop.libheimntlm.ntlm_buf) _gch.Target).length;
+ }
+ }
+
+ // Return a pointer to where data resides
+ public IntPtr Value
+ {
+ get
+ {
+ if (IsInvalid)
+ {
+ return IntPtr.Zero;
+ }
+ return ((Interop.libheimntlm.ntlm_buf) _gch.Target).data;
+ }
+ }
+
+ public SafeNtlmBufferHandle()
+ : this(0, IntPtr.Zero)
+ {
+ _isOutputBuffer = true;
+ }
+
+ public SafeNtlmBufferHandle(byte[] data) : this(data, 0, (data == null) ? 0 : data.Length)
+ {
+ }
+
+ public SafeNtlmBufferHandle(byte[] data, int offset, int count) : this(count, IntPtr.Zero)
+ {
+ if (data != null)
+ {
+ _arrayGcHandle = GCHandle.Alloc(data, GCHandleType.Pinned);
+ IntPtr address = new IntPtr(_arrayGcHandle.AddrOfPinnedObject().ToInt64() + offset);
+ Marshal.WriteIntPtr(handle, (int) Marshal.OffsetOf("data"), address);
+ }
+ }
+
+ public SafeNtlmBufferHandle(int length, IntPtr value)
+ : base(IntPtr.Zero, true)
+ {
+ Interop.libheimntlm.ntlm_buf buffer = new Interop.libheimntlm.ntlm_buf
+ {
+ length = (size_t) length,
+ data = value,
+ };
+ _gch = GCHandle.Alloc(buffer, GCHandleType.Pinned);
+ handle = _gch.AddrOfPinnedObject();
+ }
+
+ public override bool IsInvalid
+ {
+ get { return handle == IntPtr.Zero; }
+ }
+
+ //public Interop.libheimntlm.ntlm_buf ToBuffer()
+ //{
+ // return (Interop.libheimntlm.ntlm_buf) _gch.Target;
+ //}
+
+ // Note that _value should never be freed directly. For input
+ // buffer, it is owned by the caller and for output buffer,
+ // it is a by-product of some other allocation
+ protected override bool ReleaseHandle()
+ {
+ Interop.libheimntlm.ntlm_buf buffer = (Interop.libheimntlm.ntlm_buf) _gch.Target;
+
+ if (_isOutputBuffer && (buffer.data != IntPtr.Zero))
+ {
+ buffer.data = IntPtr.Zero;
+ }
+
+ if (_arrayGcHandle.IsAllocated)
+ {
+ _arrayGcHandle.Free();
+ }
+ else
+ {
+ Interop.libheimntlm.HeimNtlmFreeBuf(buffer);
+ }
+
+ _gch.Free();
+ 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(SafeNtlmBufferHandle 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.libheimntlm.EVPDigest(key, (int) key.Length, magic, magic.Length, out _digestLength);
+ _isSealingKey = isSealingKey;
+ if (_isSealingKey)
+ {
+ _cipherContext = Interop.Crypto.EvpCipherCreate(Interop.libheimntlm.EvpRc4(), digest, null, 1);
+ }
+ _gch = GCHandle.Alloc(digest, GCHandleType.Pinned);
+ handle = _gch.AddrOfPinnedObject();
+ }
+
+ public override bool IsInvalid
+ {
+ get { return handle == IntPtr.Zero; }
+ }
+
+ 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");
+ byte[] output = new byte[16];
+ Array.Clear(output, 0, output.Length);
+ byte[] hash;
+ unsafe
+ {
+ fixed (byte* outPtr = output)
+ fixed (byte* bytePtr = buffer)
+ {
+ MarshalUint(outPtr, 0x00000001); // version
+ MarshalUint(outPtr + 12, _sequenceNumber);
+ hash = Interop.libheimntlm.HMACDigest((byte*) handle.ToPointer(), (int)_digestLength, (bytePtr + offset), count,
+ outPtr + 12, 4);
+ _sequenceNumber++;
+ }
+ }
+ if ((sealingKey == null) || sealingKey.IsInvalid)
+ {
+ Array.Copy(hash, 0, output, 4, 8);
+ }
+ else
+ {
+ byte[] cipher = sealingKey.SealOrUnseal(true, hash, 0, 8);
+ Array.Copy(cipher, 0, output, 4, cipher.Length);
+ }
+ return output;
+ }
+
+ public byte[] SealOrUnseal(bool seal, byte[] buffer, int offset, int count)
+ {
+ Debug.Assert(_isSealingKey, "Cannot seal or unseal with signing key");
+ unsafe
+ {
+ fixed (byte* bytePtr = buffer)
+ {
+ // Since RC4 is XOR-based, encrypt or decrypt is relative to input data
+ byte[] output = new byte[count];
+
+ Interop.libheimntlm.EvpCipher(ref _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 SafeNtlmType2Handle() : base(IntPtr.Zero, true)
+ {
+ //do something
+ }
+
+ public override bool IsInvalid
+ {
+ get { return handle == IntPtr.Zero; }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.libheimntlm.HeimNtlmFreeType2(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+ }
+
+ ///
+ /// Wrapper around a ntlm_type3*
+ ///
+ internal sealed class SafeNtlmType3Handle : SafeHandle
+ {
+ SafeNtlmType2Handle type2handle = new SafeNtlmType2Handle();
+ public SafeNtlmType3Handle(SafeNtlmBufferHandle type2Data) : base(IntPtr.Zero, true)
+ {
+ Console.WriteLine("inside SafeNtlmType3Handle");
+ int status = Interop.libheimntlm.HeimNtlmDecodeType2(type2Data, type2handle);
+ Console.WriteLine("status: "+ status);
+ Interop.libheimntlm.HeimdalNtlmException.AssertOrThrowIfError("heim_ntlm_decode_type2 failed", status);
+ }
+
+ public override bool IsInvalid
+ {
+ get { return handle == IntPtr.Zero; }
+ }
+
+ public SafeNtlmBufferHandle GetResponse(uint flags, string username, string password, string domain,
+ out SafeNtlmBufferHandle sessionKey)
+ {
+ SafeNtlmBufferHandle outputData = new SafeNtlmBufferHandle();
+ sessionKey = null;
+ // Interop.libheimntlm.ntlm_type2 type2Message = (Interop.libheimntlm.ntlm_type2) _gch.Target;
+
+ using (SafeNtlmBufferHandle key = new SafeNtlmBufferHandle())
+ using (SafeNtlmBufferHandle lmResponse = new SafeNtlmBufferHandle())
+ using (SafeNtlmBufferHandle ntResponse = new SafeNtlmBufferHandle())
+ {
+ int status = Interop.libheimntlm.HeimNtlmNtKey(password, key);
+ Interop.libheimntlm.HeimdalNtlmException.AssertOrThrowIfError("heim_ntlm_nt_key failed", status);
+ byte[] baseSessionKey = new byte[16];
+ status = Interop.libheimntlm.HeimNtlmCalculateLm2(key.Value, key.Length, username, domain,type2handle,baseSessionKey, lmResponse);
+ Interop.libheimntlm.HeimdalNtlmException.AssertOrThrowIfError("heim_ntlm_calculate_lm2 failed",status);
+
+ status = Interop.libheimntlm.HeimNtlmCalculateNtlm1(key.Value, key.Length, type2handle, ntResponse,
+ username, domain, baseSessionKey);
+ Interop.libheimntlm.HeimdalNtlmException.AssertOrThrowIfError("heim_ntlm_calculate_ntlm1 failed", status);
+ SafeNtlmBufferHandle masterKey = null;
+ Interop.libheimntlm.ProcessType3Message(Marshal.StringToHGlobalAnsi(username),
+ Marshal.StringToHGlobalAnsi(domain),
+ flags,
+ lmResponse,
+ ntResponse,
+ type2handle,
+ key.Value,
+ key.Length,
+ ntResponse,
+ sessionKey, //TODO check who is setting this
+ out masterKey,
+ baseSessionKey,
+ (uint) baseSessionKey.Length,
+ outputData
+ );
+
+
+ Interop.libheimntlm.HeimdalNtlmException.AssertOrThrowIfError(
+ "heim_ntlm_build_ntlm1_master failed", status);
+
+ return outputData;
+ }
+ }
+
+
+ protected override bool ReleaseHandle()
+ {
+ Interop.libheimntlm.HeimNtlmFreeType2(handle);
+ SetHandle(IntPtr.Zero);
+ return true;
+ }
+ }
+}
+
+
+
+
+
diff --git a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Hmac.cs b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Hmac.cs
index cfdd81cf52bf..d19d5a3ecb5e 100644
--- a/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Hmac.cs
+++ b/src/Common/src/Interop/Unix/System.Security.Cryptography.Native/Interop.Hmac.cs
@@ -15,6 +15,9 @@ internal static partial class Crypto
[DllImport(Libraries.CryptoNative)]
internal extern static void HmacDestroy(IntPtr ctx);
+ [DllImport(Libraries.CryptoNative)]
+ internal extern static void HmacDestroy(SafeHmacCtxHandle ctx);
+
[DllImport(Libraries.CryptoNative)]
internal extern static int HmacReset(SafeHmacCtxHandle ctx);
diff --git a/src/Common/src/Interop/Unix/libgss/Interop.GssApi.cs b/src/Common/src/Interop/Unix/libgss/Interop.GssApi.cs
new file mode 100644
index 000000000000..ffcb1e141367
--- /dev/null
+++ b/src/Common/src/Interop/Unix/libgss/Interop.GssApi.cs
@@ -0,0 +1,172 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using System;
+using System.Diagnostics;
+using System.Runtime.InteropServices;
+using System.Text;
+using Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static class GssApi
+ {
+ internal static bool EstablishSecurityContext(
+ ref SafeGssContextHandle context,
+ SafeGssCredHandle credential,
+ SafeGssNameHandle targetName,
+ uint inFlags,
+ byte[] buffer,
+ out byte[] outputBuffer,
+ out uint outFlags)
+ {
+ outputBuffer = null;
+ outFlags = 0;
+
+ if (context == null)
+ {
+ context = new SafeGssContextHandle();
+ }
+
+ using (SafeGssBufferHandle inputToken = new SafeGssBufferHandle(buffer))
+ using (SafeGssBufferHandle outputToken = new SafeGssBufferHandle())
+ {
+ libgssapi.Status status;
+ libgssapi.Status minorStatus;
+
+ if (targetName == null)
+ {
+ status = libgssapi.GssAcceptSecContext(
+ out minorStatus,
+ ref context,
+ credential,
+ inputToken,
+ outputToken,
+ out outFlags);
+ }
+ else
+ {
+ status = libgssapi.GssInitSecContextSpNego(
+ out minorStatus,
+ credential,
+ ref context,
+ targetName,
+ inFlags,
+ inputToken,
+ outputToken,
+ out outFlags);
+ }
+
+ if ((status != libgssapi.Status.GSS_S_COMPLETE) && (status != libgssapi.Status.GSS_S_CONTINUE_NEEDED))
+ {
+ throw libgssapi.GssApiException.Create(SR.net_context_establishment_failed, status, minorStatus);
+ }
+
+ outputBuffer = new byte[outputToken.Length]; // Always return non-null
+ if (outputToken.Length > 0)
+ {
+ Marshal.Copy(outputToken.Value, outputBuffer, 0, outputToken.Length);
+ }
+ return (status == libgssapi.Status.GSS_S_COMPLETE) ? true : false;
+ }
+ }
+
+ internal static int Encrypt(
+ SafeGssContextHandle context,
+ bool encrypt,
+ byte[] buffer,
+ int offset,
+ int count,
+ out byte[] outputBuffer)
+ {
+ outputBuffer = null;
+ Debug.Assert((buffer != null) && (buffer.Length > 0), "Invalid input buffer passed to Encrypt");
+ Debug.Assert((offset >= 0) && (offset < buffer.Length), "Invalid input offset passed to Encrypt");
+ Debug.Assert((count > 0) && (count <= (buffer.Length - offset)), "Invalid input count passed to Encrypt");
+
+ using (SafeGssBufferHandle inputToken = new SafeGssBufferHandle(buffer, offset, count))
+ using (SafeGssBufferHandle outputToken = new SafeGssBufferHandle())
+ {
+ libgssapi.Status minorStatus;
+ libgssapi.Status status = libgssapi.GssWrap(out minorStatus, context, encrypt, inputToken, outputToken);
+ if (status != libgssapi.Status.GSS_S_COMPLETE)
+ {
+ throw libgssapi.GssApiException.Create(SR.net_context_wrap_failed, status, minorStatus);
+ }
+ outputBuffer = new byte[outputToken.Length]; // Always return non-null
+ if (outputToken.Length > 0)
+ {
+ Marshal.Copy(outputToken.Value, outputBuffer, 0, outputToken.Length);
+ }
+ return outputBuffer.Length;
+ }
+ }
+
+ internal static int Decrypt(
+ SafeGssContextHandle context,
+ byte[] buffer,
+ int offset,
+ int count)
+ {
+ Debug.Assert((buffer != null) && (buffer.Length > 0), "Invalid input buffer passed to Decrypt");
+ Debug.Assert((offset >= 0) && (offset < buffer.Length), "Invalid input offset passed to Decrypt");
+ Debug.Assert((count > 0) && (count <= (buffer.Length - offset)), "Invalid input count passed to Decrypt");
+
+ using (SafeGssBufferHandle inputToken = new SafeGssBufferHandle(buffer, offset, count))
+ using (SafeGssBufferHandle outputToken = new SafeGssBufferHandle())
+ {
+ libgssapi.Status minorStatus;
+ libgssapi.Status status = libgssapi.GssUnwrap(out minorStatus, context, inputToken, outputToken);
+ if (status != libgssapi.Status.GSS_S_COMPLETE)
+ {
+ throw libgssapi.GssApiException.Create(SR.net_context_unwrap_failed, status, minorStatus);
+ }
+
+ int length = buffer.Length - offset;
+
+ if (outputToken.Length > length)
+ {
+ throw libgssapi.GssApiException.Create(SR.Format(SR.net_context_buffer_too_small, outputToken.Length, length));
+ }
+
+ if (outputToken.Length > 0)
+ {
+ Marshal.Copy(outputToken.Value, buffer, offset, outputToken.Length);
+ }
+ return outputToken.Length;
+ }
+ }
+
+ internal static string GetSourceName(SafeGssContextHandle context)
+ {
+ libgssapi.Status minorStatus;
+ SafeGssNameHandle sourceName;
+ libgssapi.Status status = libgssapi.GssInquireSourceName(
+ out minorStatus,
+ context,
+ out sourceName);
+
+ if (status != libgssapi.Status.GSS_S_COMPLETE)
+ {
+ throw libgssapi.GssApiException.Create(status, minorStatus);
+ }
+
+ using (sourceName)
+ using (SafeGssBufferHandle outputBuffer = new SafeGssBufferHandle())
+ {
+ status = libgssapi.GssDisplayName(out minorStatus, sourceName, outputBuffer);
+ if (status != libgssapi.Status.GSS_S_COMPLETE)
+ {
+ throw libgssapi.GssApiException.Create(status, minorStatus);
+ }
+
+ // String may not be NULL terminated so PtrToStringAnsi cannot be used
+ unsafe
+ {
+ return Encoding.UTF8.GetString((byte*)outputBuffer.Value.ToPointer(), outputBuffer.Length);
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/Common/src/Interop/Unix/libgss/Interop.GssApiException.cs b/src/Common/src/Interop/Unix/libgss/Interop.GssApiException.cs
new file mode 100644
index 000000000000..eaa66f1ca5a3
--- /dev/null
+++ b/src/Common/src/Interop/Unix/libgss/Interop.GssApiException.cs
@@ -0,0 +1,94 @@
+// 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 Microsoft.Win32.SafeHandles;
+
+internal static partial class Interop
+{
+ internal static partial class libgssapi
+ {
+ internal sealed class GssApiException : Exception
+ {
+ private Status _minorStatus;
+
+ public Status MinorStatus
+ {
+ get { return _minorStatus; }
+ }
+
+ public GssApiException(string message) : base(message)
+ {
+ }
+
+ public GssApiException(Status majorStatus, Status minorStatus)
+ : this(SR.Format(SR.net_gssapi_operation_failed, majorStatus, minorStatus), majorStatus, minorStatus)
+ {
+ }
+
+ public GssApiException(string message, Status majorStatus, Status minorStatus)
+ : this(message)
+ {
+ HResult = (int)majorStatus;
+ _minorStatus = minorStatus;
+ }
+
+
+ public static GssApiException Create(string message)
+ {
+ return new GssApiException(message);
+ }
+
+ public static GssApiException Create(Status majorStatus, Status minorStatus)
+ {
+ return new GssApiException(majorStatus, minorStatus);
+ }
+
+ public static GssApiException Create(string message, Status majorStatus, Status minorStatus)
+ {
+ return new GssApiException(SR.Format(message, majorStatus, minorStatus), majorStatus, minorStatus);
+ }
+
+ public static void AssertOrThrowIfError(string message, Status majorStatus, Status minorStatus)
+ {
+ if (majorStatus != Status.GSS_S_COMPLETE)
+ {
+ GssApiException ex = Create(majorStatus, minorStatus);
+ Debug.Fail(message + ": " + ex);
+ throw ex;
+ }
+ }
+
+#if DEBUG
+ public override string ToString()
+ {
+ return Message + "\n GSSAPI status: " + GetGssApiDisplayStatus((Status)HResult, _minorStatus);
+ }
+
+ private static string GetGssApiDisplayStatus(Status majorStatus, Status minorStatus)
+ {
+ Tuple[] statusArr = { Tuple.Create(majorStatus, false), Tuple.Create(minorStatus, true) };
+ int length = statusArr.Length;
+ string[] msgStrings = new string[length];
+
+ for (int i = 0; i < length; i++)
+ {
+ using (SafeGssBufferHandle msgBuffer = new SafeGssBufferHandle())
+ {
+ Interop.libgssapi.Status minStat;
+ if (Status.GSS_S_COMPLETE != GssDisplayStatus(out minStat, statusArr[i].Item1, statusArr[i].Item2, msgBuffer))
+ {
+ continue;
+ }
+ msgStrings[i] = Marshal.PtrToStringAnsi(msgBuffer.Value);
+ }
+ }
+ return msgStrings[0] + " (" + msgStrings[1] + ") \nStatus: " + majorStatus.ToString("x8") + " (" + minorStatus.ToString("x8") + ")";
+ }
+ }
+ }
+#endif
+ }
+
diff --git a/src/Common/src/Interop/Unix/libgss/SecuritySafeHandles.cs b/src/Common/src/Interop/Unix/libgss/SecuritySafeHandles.cs
new file mode 100644
index 000000000000..37fcb0f07d8c
--- /dev/null
+++ b/src/Common/src/Interop/Unix/libgss/SecuritySafeHandles.cs
@@ -0,0 +1,94 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Win32.SafeHandles;
+
+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;
+
+namespace System.Net.Security
+{
+
+
+ internal sealed class SafeFreeGssCredentials :SafeGssCredHandle
+ {
+ public SafeFreeGssCredentials(string username, string password, string domain)
+ {
+ Create(username, password, domain);
+ }
+ }
+
+ internal sealed class SafeDeleteGssContext : SafeHandle
+ {
+ private readonly SafeGssNameHandle _targetName;
+ private SafeFreeGssCredentials _credential;
+ private SafeGssContextHandle _context;
+ private bool _encryptAndSign;
+
+ public SafeGssNameHandle TargetName
+ {
+ get { return _targetName; }
+ }
+
+ public SafeGssContextHandle GssContext
+ {
+ get { return _context; }
+ }
+
+ public bool NeedsEncryption
+ {
+ get { return _encryptAndSign; }
+ }
+
+ public SafeDeleteGssContext(string targetName, uint flags) : base(IntPtr.Zero, true)
+ {
+ // In server case, targetName can be null or empty
+ if (!String.IsNullOrEmpty(targetName))
+ {
+ _targetName = SafeGssNameHandle.Create(targetName);
+ }
+
+ _encryptAndSign = (flags & (uint)Interop.libgssapi.GssFlags.GSS_C_CONF_FLAG) != 0;
+ }
+
+ public override bool IsInvalid
+ {
+ get { return (null == _context) || _context.IsInvalid; }
+ }
+
+ public void SetHandle(SafeFreeGssCredentials credential, SafeGssContextHandle context)
+ {
+ Debug.Assert(!context.IsInvalid, "Invalid context passed to SafeDeleteGssContext");
+ _context = context;
+
+ // After context establishment is initiated, callers expect SafeDeleteGssContext
+ // to bump up the ref count.
+ // NOTE: When using default credentials, the credential handle may be invalid
+ if ((null != credential) && !credential.IsInvalid)
+ {
+ bool ignore = false;
+ _credential = credential;
+ _credential.DangerousAddRef(ref ignore);
+ }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ if ((null != _credential) && !_credential.IsInvalid)
+ {
+ _credential.DangerousRelease();
+ }
+ _context.Dispose();
+ if (_targetName != null)
+ {
+ _targetName.Dispose();
+ }
+ return true;
+ }
+ }
+
+}
diff --git a/src/Common/src/Interop/Unix/libheimntlm/Interop.HeimdalNtlm.cs b/src/Common/src/Interop/Unix/libheimntlm/Interop.HeimdalNtlm.cs
new file mode 100644
index 000000000000..ebe7100a7768
--- /dev/null
+++ b/src/Common/src/Interop/Unix/libheimntlm/Interop.HeimdalNtlm.cs
@@ -0,0 +1,60 @@
+// 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)
+ {
+ using (SafeNtlmBufferHandle data = new SafeNtlmBufferHandle())
+ {
+ int status = libheimntlm.HeimNtlmEncodeType1(flags, data);
+ libheimntlm.HeimdalNtlmException.AssertOrThrowIfError("heim_ntlm_encode_type1 failed", status);
+
+ byte[] outputBuffer = new byte[(int)data.Length]; // Always return non-null
+ if (outputBuffer.Length > 0)
+ {
+ Marshal.Copy(data.Value, outputBuffer, 0, outputBuffer.Length);
+ }
+
+ return outputBuffer;
+ }
+ }
+
+ internal static byte[] CreateAuthenticateMessage(uint flags, string username, string password, string domain,
+ byte[] type2Data, int offset, int count, out SafeNtlmBufferHandle sessionKey)
+ {
+ using (SafeNtlmBufferHandle inputData = new SafeNtlmBufferHandle(type2Data, offset, count))
+ {
+ using (SafeNtlmType3Handle outputMessage = new SafeNtlmType3Handle(inputData))
+ {
+ using (
+ SafeNtlmBufferHandle outputData = outputMessage.GetResponse(flags, username, password, domain,
+ out sessionKey))
+ {
+ byte[] outputBuffer = new byte[(int) outputData.Length]; // Always return non-null
+ if (outputBuffer.Length > 0)
+ {
+ Marshal.Copy(outputData.Value, outputBuffer, 0, outputBuffer.Length);
+ }
+ return outputBuffer;
+ }
+ }
+ }
+ }
+
+ internal static void CreateKeys(SafeNtlmBufferHandle sessionKey, out SafeNtlmKeyHandle serverSignKey, out SafeNtlmKeyHandle serverSealKey, out SafeNtlmKeyHandle clientSignKey, out SafeNtlmKeyHandle clientSealKey)
+ {
+ serverSignKey = new SafeNtlmKeyHandle(sessionKey, false, true);
+ serverSealKey = new SafeNtlmKeyHandle(sessionKey, false, false);
+ clientSignKey = new SafeNtlmKeyHandle(sessionKey, true, true);
+ clientSealKey = new SafeNtlmKeyHandle(sessionKey, true, false);
+ }
+ }
+}
+
diff --git a/src/Common/src/Interop/Unix/libheimntlm/Interop.HeimdalNtlmException.cs b/src/Common/src/Interop/Unix/libheimntlm/Interop.HeimdalNtlmException.cs
new file mode 100644
index 000000000000..25075bed71b8
--- /dev/null
+++ b/src/Common/src/Interop/Unix/libheimntlm/Interop.HeimdalNtlmException.cs
@@ -0,0 +1,47 @@
+// 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 libheimntlm
+ {
+ 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 HeimdalNtlmException Create(string message)
+ {
+ return new HeimdalNtlmException(message);
+ }
+
+ public static HeimdalNtlmException Create(int error)
+ {
+ return new HeimdalNtlmException(error);
+ }
+
+ public static void AssertOrThrowIfError(string message, int error)
+ {
+ if (error != 0)
+ {
+ var ex = Create(error);
+ Debug.Fail(message + ": " + ex);
+ throw ex;
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/Common/src/Interop/Unix/libheimntlm/SecuritySafeHandles.cs b/src/Common/src/Interop/Unix/libheimntlm/SecuritySafeHandles.cs
new file mode 100644
index 000000000000..7800cc8b6c94
--- /dev/null
+++ b/src/Common/src/Interop/Unix/libheimntlm/SecuritySafeHandles.cs
@@ -0,0 +1,135 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+using Microsoft.Win32.SafeHandles;
+
+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;
+
+namespace System.Net.Security
+{
+
+internal sealed class SafeFreeNtlmCredentials : SafeHandle
+ {
+ private readonly string _username;
+ private readonly string _password;
+ private readonly string _domain;
+
+ public string UserName
+ {
+ get { return _username; }
+ }
+
+ public string Password
+ {
+ get { return _password; }
+ }
+
+ public string Domain
+ {
+ get { return _domain; }
+ }
+
+ public SafeFreeNtlmCredentials(string username, string password, string domain)
+ : base(IntPtr.Zero, false)
+ {
+ _username = username;
+ _password = password;
+ _domain = domain;
+ }
+
+ public override bool IsInvalid
+ {
+ get { return false; }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ return true;
+ }
+ }
+
+ internal sealed class SafeDeleteNtlmContext : SafeHandle
+ {
+ private readonly SafeFreeNtlmCredentials _credential;
+ private readonly uint _flags;
+ private SafeNtlmKeyHandle _serverSignKey;
+ private SafeNtlmKeyHandle _serverSealKey;
+ private SafeNtlmKeyHandle _clientSignKey;
+ private SafeNtlmKeyHandle _clientSealKey;
+
+ public uint Flags
+ {
+ get { return _flags; }
+ }
+
+ public SafeDeleteNtlmContext(SafeFreeNtlmCredentials credential, uint flags)
+ : base(IntPtr.Zero, true)
+ {
+ bool ignore = false;
+ credential.DangerousAddRef(ref ignore);
+ _credential = credential;
+ _flags = flags;
+ }
+
+ public override bool IsInvalid
+ {
+ get { return (null == _credential) || _credential.IsInvalid; }
+ }
+
+ public void SetKeys(SafeNtlmBufferHandle sessionKey)
+ {
+ Interop.HeimdalNtlm.CreateKeys(sessionKey, out _serverSignKey, out _serverSealKey, out _clientSignKey, out _clientSealKey);
+ }
+
+ public byte[] MakeSignature(bool isSend, byte[] buffer, int offset, int count)
+ {
+ if (isSend)
+ {
+ return _clientSignKey.Sign(_clientSealKey, buffer, offset, count);
+ }
+ else
+ {
+ return _serverSignKey.Sign(_serverSealKey, buffer, offset, count);
+ }
+ }
+
+ public byte[] EncryptOrDecrypt(bool isEncrypt, byte[] buffer, int offset, int count)
+ {
+ if (isEncrypt)
+ {
+ return _clientSealKey.SealOrUnseal(true, buffer, offset, count);
+ }
+ else
+ {
+ return _serverSealKey.SealOrUnseal(false, buffer, offset, count);
+ }
+ }
+
+ protected override bool ReleaseHandle()
+ {
+ _credential.DangerousRelease();
+ if ((null != _clientSignKey) && !_clientSignKey.IsInvalid)
+ {
+ _clientSignKey.Dispose();
+ }
+ if ((null != _clientSealKey) && !_clientSealKey.IsInvalid)
+ {
+ _clientSealKey.Dispose();
+ }
+ if ((null != _serverSignKey) && !_serverSignKey.IsInvalid)
+ {
+ _serverSignKey.Dispose();
+ }
+ if ((null != _serverSealKey) && !_serverSealKey.IsInvalid)
+ {
+ _serverSealKey.Dispose();
+ }
+ return true;
+ }
+ }
+}
diff --git a/src/Native/CMakeLists.txt b/src/Native/CMakeLists.txt
index a03cfea95ba0..82d6076d1840 100644
--- a/src/Native/CMakeLists.txt
+++ b/src/Native/CMakeLists.txt
@@ -82,7 +82,8 @@ endif ()
include(configure.cmake)
-add_subdirectory(System.IO.Compression.Native)
+
add_subdirectory(System.Native)
add_subdirectory(System.Net.Http.Native)
+add_subdirectory(System.Net.Security.Native)
add_subdirectory(System.Security.Cryptography.Native)
diff --git a/src/Native/System.Net.Security.Native/CMakeLists.txt b/src/Native/System.Net.Security.Native/CMakeLists.txt
new file mode 100644
index 000000000000..8cf87d646985
--- /dev/null
+++ b/src/Native/System.Net.Security.Native/CMakeLists.txt
@@ -0,0 +1,55 @@
+project(System.Net.Security.Native)
+
+set(CMAKE_INCLUDE_CURRENT_DIR ON)
+
+add_definitions(-DPIC=1)
+
+if(APPLE)
+ add_definitions(-D__APPLE_USE_RFC_3542)
+ add_definitions(-DTARGET_OS_MAC=1)
+else()
+ add_definitions(-DTARGET_OS_MAC=0)
+endif()
+
+
+
+find_library(LIBGSS NAMES gssapi_krb5 PATHS /usr/lib/x86_64-linux-gnu/mit-krb5)
+
+if(LIBGSS STREQUAL LIBGSS-NOTFOUND)
+
+ message(SEND_ERROR "!!! Cannot find libkrb5-dev and System.Net.Security.Native cannot build without it. Try installing libkrb5-dev (or the appropriate package for your platform) !!!")
+ return()
+endif()
+
+find_library(HEIMDAL NAMES heimntlm)
+
+if(HEIMDAL STREQUAL HEIMDAL-NOTFOUND)
+ message(SEND_ERROR "!!! Cannot find heimdal-dev and System.Net.Security.Native cannot build without it. Try installing heimdal-dev (or the appropriate package for your platform) !!!")
+ return()
+endif()
+
+set(NATIVENTLM_SOURCES
+ pal_ntlmapi.cpp
+)
+
+set(NATIVEGSS_SOURCES
+ pal_gssapi.cpp
+)
+
+include_directories(/usr/include)
+include_directories(/usr/include/mit-krb5)
+include_directories(/usr/include/heimdal)
+
+add_library(System.Net.Security.Native
+ SHARED
+ ${NATIVENTLM_SOURCES}
+ ${NATIVEGSS_SOURCES}
+)
+
+
+target_link_libraries(System.Net.Security.Native
+ ${HEIMDAL}
+ ${LIBGSS}
+)
+
+install (TARGETS System.Net.Security.Native DESTINATION .)
\ No newline at end of file
diff --git a/src/Native/System.Net.Security.Native/pal_gssapi.cpp b/src/Native/System.Net.Security.Native/pal_gssapi.cpp
new file mode 100644
index 000000000000..c5021cf5f420
--- /dev/null
+++ b/src/Native/System.Net.Security.Native/pal_gssapi.cpp
@@ -0,0 +1,106 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#include "pal_gssapi.h"
+#include
+#include
+
+static_assert(PAL_GSS_COMPLETE == GSS_S_COMPLETE, "");
+static_assert(PAL_GSS_CONTINUE_NEEDED == GSS_S_CONTINUE_NEEDED, "");
+
+static char gss_mech_value[] = "\x2b\x06\x01\x05\x05\x02";
+
+static gss_OID_desc gss_mech_spnego_OID_desc = { 6, static_cast(gss_mech_value) };
+
+static gss_OID gss_mech_spnego_OID = &gss_mech_spnego_OID_desc;
+
+static gss_OID_set_desc gss_mech_spnego_OID_set_desc = { 1, &gss_mech_spnego_OID_desc };
+
+static gss_OID_set gss_mech_spnego_OID_set = &gss_mech_spnego_OID_set_desc;
+
+extern "C" uint32_t GssAcceptSecContext(uint32_t* minorStatus, gss_ctx_id_t* contextHandle, gss_cred_id_t acceptorCredHandle,
+ gss_buffer_t inputToken, gss_buffer_t outputToken, uint32_t* retFlags)
+{
+ return gss_accept_sec_context(minorStatus, contextHandle, acceptorCredHandle, inputToken, 0, NULL, NULL, outputToken, retFlags, NULL, NULL);
+}
+
+extern "C" uint32_t GssAcquireCredSpNego(uint32_t* minorStatus, gss_name_t desiredName, bool isInitiate, gss_cred_id_t* outputCredHandle)
+{
+ gss_cred_usage_t credUsage = isInitiate ? GSS_C_INITIATE : GSS_C_ACCEPT;
+ return gss_acquire_cred(minorStatus, desiredName, 0, gss_mech_spnego_OID_set, credUsage, outputCredHandle, NULL, NULL);
+}
+
+extern "C" uint32_t GssDeleteSecContext(uint32_t* minorStatus, gss_ctx_id_t* contextHandle)
+{
+ return gss_delete_sec_context(minorStatus, contextHandle, 0);
+}
+
+extern "C" uint32_t GssDisplayName(uint32_t* minorStatus, gss_name_t inputName, gss_buffer_t outputNameBuffer)
+{
+ return gss_display_name(minorStatus, inputName, outputNameBuffer, NULL);
+}
+
+extern "C" uint32_t GssDisplayStatus(uint32_t* minorStatus, uint32_t statusValue, bool isGssMechCode, gss_buffer_t statusString)
+{
+ int statusType = isGssMechCode ? GSS_C_MECH_CODE : GSS_C_GSS_CODE;
+ return gss_display_status(minorStatus, statusValue, statusType, 0, NULL, statusString);
+}
+
+extern "C" uint32_t GssImportNtUserName(uint32_t* minorStatus, char* inputName, gss_name_t* outputName)
+{
+ gss_buffer_desc inputNameBuffer{ strlen(inputName), inputName };
+ return gss_import_name(minorStatus, &inputNameBuffer, GSS_C_NT_USER_NAME, outputName);
+}
+
+extern "C" uint32_t GssInitSecContextSpNego(uint32_t* minorStatus, gss_cred_id_t claimantCredHandle, gss_ctx_id_t* contextHandle,
+ gss_name_t targetName, uint32_t reqFlags, gss_buffer_t inputToken, gss_buffer_t outputToken, uint32_t* retFlags)
+{
+ return gss_init_sec_context(minorStatus, claimantCredHandle, contextHandle, targetName, gss_mech_spnego_OID, reqFlags,
+ 0, 0, inputToken, NULL, outputToken, retFlags, NULL);
+}
+
+extern "C" uint32_t GssInquireSourceName(uint32_t* minorStatus, gss_ctx_id_t contextHandle, gss_name_t* srcName)
+{
+ return gss_inquire_context(minorStatus, contextHandle, srcName, NULL, NULL, NULL, NULL, NULL, NULL);
+}
+
+extern "C" uint32_t GssReleaseCred(uint32_t* minorStatus, gss_cred_id_t* credHandle)
+{
+ return gss_release_cred(minorStatus, credHandle);
+}
+
+extern "C" uint32_t GssReleaseBuffer(uint32_t* minor_status, gss_buffer_t buffer)
+{
+ return gss_release_buffer(minor_status, buffer);
+}
+
+extern "C" uint32_t GssReleaseName(uint32_t* minorStatus, gss_name_t* inputName)
+{
+ return gss_release_name(minorStatus, inputName);
+}
+
+extern "C" uint32_t GssWrap(uint32_t* minorStatus, gss_ctx_id_t contextHandle, bool isEncrypt, gss_buffer_t inputMessageBuffer,
+ gss_buffer_t outputMessageBuffer)
+{
+ int confState;
+ int confReqFlag = isEncrypt ? 1 : 0;
+ uint32_t retVal = gss_wrap(minorStatus, contextHandle, confReqFlag, GSS_C_QOP_DEFAULT, inputMessageBuffer, &confState, outputMessageBuffer);
+ assert((confState == 0) == (confReqFlag == 0));
+ return retVal;
+}
+
+extern "C" uint32_t GssUnwrap(uint32_t* minorStatus, gss_ctx_id_t contextHandle, gss_buffer_t inputMessageBuffer,
+ gss_buffer_t outputMessageBuffer)
+{
+ return gss_unwrap(minorStatus, contextHandle, inputMessageBuffer, outputMessageBuffer, NULL, NULL);
+}
+
+extern "C" uint32_t GssAcquireCredWithPasswordSpNego(uint32_t* minorStatus, const gss_name_t desiredName, char* password, bool isInitiate,
+ gss_cred_id_t* outputCredHandle)
+{
+ gss_cred_usage_t credUsage = isInitiate ? GSS_C_INITIATE : GSS_C_ACCEPT;
+ gss_buffer_desc passwordBuffer{ strlen(password), password };
+ return gss_acquire_cred_with_password(minorStatus, desiredName, &passwordBuffer, 0, gss_mech_spnego_OID_set,
+ credUsage, outputCredHandle, NULL, NULL);
+}
+
diff --git a/src/Native/System.Net.Security.Native/pal_gssapi.h b/src/Native/System.Net.Security.Native/pal_gssapi.h
new file mode 100644
index 000000000000..8edf6c675a74
--- /dev/null
+++ b/src/Native/System.Net.Security.Native/pal_gssapi.h
@@ -0,0 +1,90 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#pragma once
+#include "pal_types.h"
+#include
+
+enum GssStatus : uint32_t
+{
+ PAL_GSS_COMPLETE = 0,
+ PAL_GSS_CONTINUE_NEEDED = 1
+};
+
+/*
+Shims the gss_release_buffer method.
+*/
+extern "C" uint32_t GssReleaseBuffer(uint32_t* minor_status, gss_buffer_t buffer);
+
+/*
+Shims the gss_display_status method.
+*/
+extern "C" uint32_t GssDisplayStatus(uint32_t* minorStatus, uint32_t statusValue, bool isGssMechCode, gss_buffer_t statusString);
+
+/*
+Shims the gss_display_name method.
+*/
+extern "C" uint32_t GssDisplayName(uint32_t* minorStatus, gss_name_t inputName, gss_buffer_t outputNameBuffer);
+
+/*
+Shims the gss_import_name method.
+This method will import NT type usernames only.
+*/
+extern "C" uint32_t GssImportNtUserName(uint32_t* minorStatus, char* inputName, gss_name_t* outputName);
+
+/*
+Shims the gss_release_name method.
+*/
+extern "C" uint32_t GssReleaseName(uint32_t* minorStatus, gss_name_t* inputName);
+
+/*
+Shims the gss_acquire_cred method with SPNEGO oids.
+*/
+extern "C" uint32_t GssAcquireCredSpNego(uint32_t* minorStatus, gss_name_t desiredName, bool isInitiate, gss_cred_id_t* outputCredHandle);
+
+/*
+Shims the gss_release_cred method.
+*/
+extern "C" uint32_t GssReleaseCred(uint32_t* minorStatus, gss_cred_id_t* credHandle);
+
+/*
+Shims the gss_init_sec_context method with SPNEGO oids.
+*/
+extern "C" uint32_t GssInitSecContextSpNego(uint32_t* minorStatus, gss_cred_id_t claimantCredHandle, gss_ctx_id_t* contextHandle,
+ gss_name_t targetName, uint32_t reqFlags, gss_buffer_t inputToken, gss_buffer_t outputToken, uint32_t* retFlags);
+
+/*
+Shims the gss_accept_sec_context method.
+*/
+extern "C" uint32_t GssAcceptSecContext(uint32_t* minorStatus, gss_ctx_id_t* contextHandle, gss_cred_id_t acceptorCredHandle,
+ gss_buffer_t inputToken, gss_buffer_t outputToken, uint32_t* retFlags);
+
+/*
+Shims the gss_delete_sec_context method.
+*/
+extern "C" uint32_t GssDeleteSecContext(uint32_t* minorStatus, gss_ctx_id_t* contextHandle);
+
+/*
+Shims the gss_wrap method.
+*/
+extern "C" uint32_t GssWrap(uint32_t* minorStatus, gss_ctx_id_t contextHandle, bool isEncrypt, gss_buffer_t inputMessageBuffer,
+ gss_buffer_t outputMessageBuffer);
+
+/*
+Shims the gss_unwrap method.
+*/
+extern "C" uint32_t GssUnwrap(uint32_t* minorStatus, gss_ctx_id_t contextHandle, gss_buffer_t inputMessageBuffer,
+ gss_buffer_t outputMessageBuffer);
+
+/*
+Shims the gss_inquire_context method.
+*/
+extern "C" uint32_t GssInquireSourceName(uint32_t* minorStatus, gss_ctx_id_t contextHandle, gss_name_t* srcName);
+
+
+/*
+Shims the gss_acquire_cred_with_password method.
+*/
+extern "C" uint32_t GssAcquireCredWithPasswordSpNego(uint32_t* minorStatus, const gss_name_t desiredName, char* password, bool isInitiate,
+ gss_cred_id_t* outputCredHandle);
+
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..cb38b6dc925c
--- /dev/null
+++ b/src/Native/System.Net.Security.Native/pal_ntlmapi.cpp
@@ -0,0 +1,120 @@
+// 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 "pal_ntlmapi.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, "");
+
+extern "C" void HeimNtlmFreeBuf(ntlm_buf* data)
+{
+ heim_ntlm_free_buf(data);
+}
+
+extern "C" int32_t HeimNtlmEncodeType1(uint32_t flags, ntlm_buf* data)
+{
+
+ ntlm_type1 type1 = { 0 };
+ type1.flags = flags;
+ return heim_ntlm_encode_type1(&type1, data);
+
+}
+
+extern "C" int32_t HeimNtlmEncodeType3(ntlm_type3* type3, ntlm_buf* data, size_t* size)
+{
+ return heim_ntlm_encode_type3(type3, data, size);
+}
+
+extern "C" int32_t HeimNtlmDecodeType2(ntlm_buf* data, ntlm_type2* type2)
+{
+ return heim_ntlm_decode_type2(data, type2);
+}
+
+extern "C" void HeimNtlmFreeType2(ntlm_type2* type2)
+{
+ heim_ntlm_free_type2(type2);
+}
+
+extern "C" int32_t HeimNtlmCalculateLm2(const void * key , size_t len, const char* username, const char* target, ntlm_type2* type2, unsigned char* ntlmv2 , ntlm_buf * data)
+{
+ return heim_ntlm_calculate_lm2(key, len, username, target, type2->challenge, ntlmv2, data);
+}
+
+extern "C" int32_t HeimNtlmCalculateNtlm1(void * key , size_t len, ntlm_type2* type2, ntlm_buf* data, const char* username, const char* target, unsigned char* ntlmv2)
+{
+ if (type2->targetinfo.length == 0)
+ {
+ return heim_ntlm_calculate_ntlm1(key, len, type2->challenge, data);
+ }
+ else
+ {
+ return heim_ntlm_calculate_ntlm2(key, len, username, target, type2->challenge, &type2->targetinfo, ntlmv2, data);
+ }
+
+}
+
+extern "C" int32_t HeimNtlmCalculateNtlm2(const void * key, size_t len, const char* username, const char* target, ntlm_type2* type2, unsigned char* ntlmv2, ntlm_buf* data)
+{
+ return heim_ntlm_calculate_ntlm2(key, len, username, target, type2->challenge, &type2->targetinfo, ntlmv2, data);
+}
+
+extern "C" int32_t HeimNtlmBuildNtlm1Master(void * key, size_t size, ntlm_buf* session, ntlm_buf* master)
+{
+ return heim_ntlm_build_ntlm1_master(key, size, session, master);
+}
+
+extern "C" int32_t HeimNtlmBuildNtlm2Master(void * key, size_t size, ntlm_buf* blob, ntlm_buf* session, ntlm_buf* master)
+{
+ return heim_ntlm_build_ntlm2_master(key, size, blob, session, master);
+}
+
+extern "C" int32_t ProcessType3Message(char* username, char* domain, uint32_t flags, ntlm_buf* lm, ntlm_buf* ntlm, ntlm_type2* type2, void * key, size_t size, ntlm_buf* ntResponse, ntlm_buf* session, ntlm_buf* master, void * baseSessionKey, size_t baseSessionKeyLen, ntlm_buf* outputData)
+{
+ char* ws = nullptr;
+ ntlm_type3 type3 = { 0 };
+ type3.username = username;
+ type3.targetname = domain;
+ type3.lm = *lm;
+ type3.ntlm = *ntlm;
+ type3.ws = ws;
+ type3.flags = flags;
+
+ int32_t status = 0;
+
+ if (type2->targetinfo.length == 0)
+ {
+ ntlm_buf* masterKey = (ntlm_buf *)malloc(sizeof(ntlm_buf));
+ status = heim_ntlm_build_ntlm1_master(key, size, session, masterKey);
+ heim_ntlm_free_buf(masterKey);
+ heim_ntlm_free_buf(session);
+ if (status != 0)
+ return status;
+ }
+ else
+ {
+ // Only first 16 bytes of the NTLMv2 response should be passed
+ ntlm_buf blob = { 0 };
+ blob.length = 16;
+ blob.data = ntResponse->data;
+ status = HeimNtlmBuildNtlm2Master(baseSessionKey, baseSessionKeyLen, &blob, session, master);
+ if (status != 0)
+ return status;
+ }
+
+ type3.sessionkey = *master;
+ size_t micOffset = 0;
+ status = heim_ntlm_encode_type3(&type3, outputData, &micOffset);
+ if (status != 0)
+ return status;
+
+ return 0;
+ }
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..e39c2bde2406
--- /dev/null
+++ b/src/Native/System.Net.Security.Native/pal_ntlmapi.h
@@ -0,0 +1,78 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+#pragma once
+
+#include
+#include
+#include "pal_types.h"
+#include "heimdal/heimntlm.h"
+
+
+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,
+};
+
+/*
+Shims heim_ntlm_free_buf method.
+*/
+extern "C" void HeimNtlmFreeBuf(ntlm_buf* data);
+
+/*
+Shims heim_ntlm_encode_type1 method.
+*/
+extern "C" int32_t HeimNtlmEncodeType1(uint32_t flags, ntlm_buf* data);
+
+/*
+Shims heim_ntlm_encode_type3 method.
+*/
+extern "C" int32_t HeimNtlmEncodeType3(ntlm_type3* type3, ntlm_buf* data, size_t* size);
+
+/*
+Shims heim_ntlm_decode_type2 method.
+*/
+extern "C" int32_t HeimNtlmDecodeType2(ntlm_buf* data, ntlm_type2* type2);
+
+/*
+Shims heim_ntlm_free_type2 method.
+*/
+extern "C" void HeimNtlmFreeType2(ntlm_type2* type2);
+
+/*
+Shims heim_ntlm_calculate_lm2 method.
+*/
+extern "C" int32_t HeimNtlmCalculateLm2(const void * key, size_t len, const char* username, const char* target, ntlm_type2* type2, unsigned char* ntlmv2, ntlm_buf * data);
+
+/*
+Shims heim_ntlm_calculate_ntlm1 method.
+*/
+extern "C" int32_t HeimNtlmCalculateNtlm1(void * key, size_t len, ntlm_type2* type2, ntlm_buf* data, const char* username, const char* target, unsigned char* ntlmv2);
+
+/*
+Shims the heim_ntlm_calculate_ntlm2 method.
+*/
+extern "C" int32_t HeimNtlmCalculateNtlm2(const void * key, size_t len, const char* username, const char* target, ntlm_type2* type2, unsigned char* ntlmv2, ntlm_buf* data);
+
+/*
+Shims heim_ntlm_build_ntlm1_master method.
+*/
+extern "C" int32_t HeimNtlmBuildNtlm1Master(void * key, size_t size, ntlm_buf* session, ntlm_buf* master);
+
+/*
+Shims heim_ntlm_build_ntlm2_master method.
+*/
+extern "C" int32_t HeimNtlmBuildNtlm2Master(void * key, size_t size, ntlm_buf* blob, ntlm_buf* session, ntlm_buf* master);
+
+/*
+Implements Typ3 msg proccessing logic
+*/
+extern "C" int32_t ProcessType3Message(char* username, char* domain, uint32_t flags, ntlm_buf* lm, ntlm_buf* ntlm, ntlm_type2* type2, void * key, size_t size, ntlm_buf* ntResponse, ntlm_buf* session, ntlm_buf* master, void * baseSessionKey, size_t baseSessionKeyLen, ntlm_buf* outputData);
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 58156b1b06f2..252beb9123b3 100644
--- a/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.cpp
+++ b/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.cpp
@@ -9,6 +9,16 @@
#define SUCCESS 1
#define KEEP_CURRENT_DIRECTION -1
+extern "C" void EvpCipherInit(EVP_CIPHER_CTX* ctx)
+{
+ EVP_CIPHER_CTX_init(ctx);
+}
+
+extern "C" void EvpCipher(EVP_CIPHER_CTX* ctx, unsigned char* out, const unsigned char *in, uint32_t inl)
+{
+ EVP_Cipher(ctx, out, in, inl);
+}
+
extern "C" EVP_CIPHER_CTX* EvpCipherCreate(const EVP_CIPHER* type, uint8_t* key, unsigned char* iv, int32_t enc)
{
std::unique_ptr ctx(new (std::nothrow) EVP_CIPHER_CTX);
@@ -110,3 +120,8 @@ extern "C" const EVP_CIPHER* EvpAes256Cbc()
{
return EVP_aes_256_cbc();
}
+
+extern "C" const EVP_CIPHER* EvpRc4()
+{
+ return EVP_rc4();
+}
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 4b39e7dd1aea..8f6a7c51fa82 100644
--- a/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.h
+++ b/src/Native/System.Security.Cryptography.Native/pal_evp_cipher.h
@@ -3,6 +3,24 @@
#include "pal_types.h"
#include
+#include
+
+/*
+Function:
+EvpCipherInit
+
+Direct shim to EVP_CIPHER_CTX_init.
+*/
+extern "C" void EvpCipherInit(EVP_CIPHER_CTX* ctx);
+
+/*
+Function:
+EvpCipher
+
+Direct shim to EVP_Cipher.
+*/
+extern "C" void EvpCipher(EVP_CIPHER_CTX* ctx, unsigned char* out, const unsigned char *in, uint32_t inl);
+
/*
Creates and initializes an EVP_CIPHER_CTX with the given args.
@@ -108,3 +126,11 @@ EvpAes256Cbc
Direct shim to EVP_aes_256_cbc.
*/
extern "C" const EVP_CIPHER* EvpAes256Cbc();
+
+/*
+Function:
+EvpRc4
+
+Direct shim to EVP_rc4.
+*/
+extern "C" const EVP_CIPHER* EvpRc4();
diff --git a/src/System.Net.Security/src/Resources/Strings.resx b/src/System.Net.Security/src/Resources/Strings.resx
index 56d698c86d86..3f73a2568b9c 100644
--- a/src/System.Net.Security/src/Resources/Strings.resx
+++ b/src/System.Net.Security/src/Resources/Strings.resx
@@ -375,4 +375,22 @@
SSL certificate returned is invalid, OpenSSL error - {0}.
+
+ GSSAPI operation failed with status: {0} (Minor status: {1})
+
+
+ GSSAPI security context establishment failed with status: {0} (Minor status: {1})
+
+
+ GSSAPI encryption or signing failed with status: {0} (Minor status: {1})
+
+
+ GSSAPI decryption or signature verification failed with status: {0} (Minor status: {1})
+
+
+ Insufficient buffer space. Required: {0} Actual: {1}
+
+
+ Heimntlm operation failed with status: {0})
+
diff --git a/src/System.Net.Security/src/System.Net.Security.csproj b/src/System.Net.Security/src/System.Net.Security.csproj
index f2827a9c9e23..19efb0a93830 100644
--- a/src/System.Net.Security/src/System.Net.Security.csproj
+++ b/src/System.Net.Security/src/System.Net.Security.csproj
@@ -4,12 +4,14 @@
System.Net.Security
4.0.0.0
- Library
+ Exe
{89F37791-6254-4D60-AB96-ACD3CCA0E771}
true
$(DefineConstants);FEATURE_CORECLR
dnxcore50
true
+ v4.5
+ Client
win\project.json
@@ -34,6 +36,9 @@
+
+
+
@@ -209,6 +214,7 @@
+
@@ -226,6 +232,23 @@
Common\Interop\Unix\libssl\Interop.OpenSsl.cs
+
+ Common\Interop\Unix\System.Net.Security.Native\Interop.Ntlm.cs
+
+
+
+ Common\Interop\Unix\libheimntlm\Interop.HeimdalNtlm.cs
+
+
+ Common\Interop\Unix\libheimntlm\Interop.HeimdalNtlmException.cs
+
+
+ Common\Interop\Unix\libheimntlm\SecuritySafeHandles.cs
+
+
+ Common\Interop\Unix\libgss\SecuritySafeHandles.cs
+
+
Common\Interop\Unix\libssl\Interop.X509ChannelBindingHash.cs
@@ -268,6 +291,19 @@
Common\Interop\Unix\System.Security.Cryptography.Native\Interop.X509StoreCtx.cs
+
+ Common\Interop\Unix\System.Security.Cryptography.Native\Interop.Hmac.cs
+
+
+ Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.cs
+
+
+ Microsoft\Win32\SafeHandles\SafeEvpMdCtxHandle.Unix.cs
+
+
+
+ Common\Interop\Unix\System.Security.Cryptography.Native\Interop.EVP.Cipher.cs
+
Common\Interop\Unix\System.Net.Security.Native\Interop.Initialization.cs
@@ -280,6 +316,9 @@
Common\Microsoft\Win32\SafeHandles\X509ExtensionSafeHandles.Unix.cs
+
+ Common\Microsoft\Win32\SafeHandles\SafeEvpCipherCtxHandle.Unix.cs
+
Common\Microsoft\Win32\SafeHandles\SafeInteriorHandle.cs
@@ -289,6 +328,20 @@
Common\Microsoft\Win32\SafeHandles\Asn1SafeHandles.Unix.cs
+
+ Common\Microsoft\Win32\SafeHandles\SafeHmacCtxHandle.Unix.cs
+
+
+
+ Common\Interop\Unix\System.Net.Security.Native\Interop.Gss.cs
+
+
+ Common\Interop\Unix\libgss\Interop.GssApi.cs
+
+
+ Common\Interop\Unix\libgss\Interop.GssApiException.cs
+
+
diff --git a/src/System.Net.Security/src/System/Net/NegotiateStreamPal.Unix.cs b/src/System.Net.Security/src/System/Net/NegotiateStreamPal.Unix.cs
new file mode 100644
index 000000000000..139e8581f84f
--- /dev/null
+++ b/src/System.Net.Security/src/System/Net/NegotiateStreamPal.Unix.cs
@@ -0,0 +1,251 @@
+// 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.Net.Security;
+using System.Runtime.InteropServices;
+using Microsoft.Win32.SafeHandles;
+
+namespace System.Net
+{
+ // Depending on PAL refactoring, this will either be part of a class that implements
+ // SSPIInterfaceNego or Unix-specific files (eg. _NTAuthenticationPal.Unix.cs) will
+ // call into methods of this class
+ internal static class NegotiateStreamPal
+ {
+ public static SecurityStatusPal AcquireCredentialsHandle(
+ string moduleName,
+ bool isInBoundCred,
+ string username,
+ string password,
+ string domain,
+ out SafeHandle outCredential)
+ {
+ if (isInBoundCred || string.IsNullOrEmpty(username))
+ {
+ // In server case, only the keytab file (eg. /etc/krb5.keytab) is used
+ // In client case, equivalent of default credentials is to use previous,
+ // unexpired Kerberos TGT to get service-specific ticket.
+ outCredential = new SafeFreeGssCredentials(string.Empty, string.Empty, string.Empty);
+ }
+ else if (string.Equals(moduleName, "NTLM"))
+ {
+ outCredential = new SafeFreeNtlmCredentials(username, password, domain);
+ }
+ else
+ {
+ outCredential = new SafeFreeGssCredentials(username, password, domain);
+ // TODO (Issue #3717): Fall back to NTLM if Kerberos ticket cannot be obtained
+ }
+ return SecurityStatusPal.OK;
+ }
+
+ public static SecurityStatusPal AcquireDefaultCredential(string moduleName, bool isInBoundCred, out SafeHandle outCredential)
+ {
+ return AcquireCredentialsHandle(moduleName, isInBoundCred, string.Empty, string.Empty, string.Empty, out outCredential);
+ }
+
+ public static SecurityStatusPal AcceptSecurityContext(
+ SafeHandle credential,
+ ref SafeHandle context,
+ SecurityBuffer inputBuffer,
+ uint inFlags,
+ uint endianNess,
+ SecurityBuffer outputBuffer,
+ ref uint outFlags)
+ {
+ return EstablishSecurityContext((SafeFreeGssCredentials)credential, ref context, string.Empty, inFlags, inputBuffer, outputBuffer, ref outFlags);
+ }
+
+ public static SecurityStatusPal InitializeSecurityContext(
+ SafeHandle credential,
+ ref SafeHandle context,
+ string targetName,
+ uint inFlags,
+ uint endianNess,
+ SecurityBuffer[] inputBuffers,
+ SecurityBuffer outputBuffer,
+ ref uint outFlags)
+ {
+ // TODO (Issue #3718): The second buffer can contain a channel binding which is not yet supported
+ if (inputBuffers.Length > 1)
+ {
+ throw new NotImplementedException("No support for channel binding on non-Windows");
+ }
+
+ if (IsNtlmClient(targetName, credential))
+ {
+ return InitializeNtlmSecurityContext((SafeFreeNtlmCredentials)credential, ref context, inFlags, inputBuffers[0], outputBuffer);
+ }
+ return EstablishSecurityContext((SafeFreeGssCredentials)credential, ref context, targetName, inFlags, inputBuffers[0], outputBuffer, ref outFlags);
+ }
+
+ public static int Encrypt(SafeHandle securityContext, byte[] buffer, int offset, int count, ref byte[] output, uint sequenceNumber)
+ {
+ if (securityContext is SafeDeleteGssContext)
+ {
+ // Sequence number is not used by libgssapi
+ SafeDeleteGssContext gssContext = securityContext as SafeDeleteGssContext;
+ return Interop.GssApi.Encrypt(gssContext.GssContext, gssContext.NeedsEncryption, buffer, offset, count, out output);
+ }
+
+ // TODO (Issue# 3717): Figure out why sign verification on peer fails
+ SafeDeleteNtlmContext context = securityContext as SafeDeleteNtlmContext;
+ byte[] cipher = context.EncryptOrDecrypt(true, buffer, offset, count);
+ byte[] signature = context.MakeSignature(true, buffer, offset, count);
+ output = new byte[cipher.Length + signature.Length];
+ Array.Copy(signature, 0, output, 0, signature.Length);
+ Array.Copy(cipher, 0, output, signature.Length, cipher.Length);
+ return output.Length;
+ }
+
+ public static int Decrypt(SafeHandle securityContext, byte[] buffer, int offset, int count, out int newOffset, uint sequenceNumber)
+ {
+ if (securityContext is SafeDeleteGssContext)
+ {
+ // Sequence number is not used by libgssapi
+ newOffset = offset;
+ return Interop.GssApi.Decrypt(((SafeDeleteGssContext)securityContext).GssContext, buffer, offset, count);
+ }
+ SafeDeleteNtlmContext context = securityContext as SafeDeleteNtlmContext;
+ byte[] message = context.EncryptOrDecrypt(false, buffer, (offset + 16), (count - 16));
+ Array.Copy(message, 0, buffer, (offset + 16), message.Length);
+ return VerifySignature(securityContext, buffer, offset, count, out newOffset, sequenceNumber);
+ }
+
+ public static int MakeSignature(SafeHandle securityContext, byte[] buffer, int offset, int count, ref byte[] output, uint sequenceNumber)
+ {
+ if (securityContext is SafeDeleteGssContext)
+ {
+ // Sequence number is not used by libgssapi
+ SafeDeleteGssContext context = ((SafeDeleteGssContext)securityContext);
+ return Interop.GssApi.Encrypt(context.GssContext, context.NeedsEncryption, buffer, offset, count, out output);
+ }
+ byte[] signature = ((SafeDeleteNtlmContext) securityContext).MakeSignature(true, buffer, offset, count);
+ output = new byte[signature.Length + count];
+ Array.Copy(signature, 0, output, 0, signature.Length);
+ Array.Copy(buffer, offset, output, signature.Length, count);
+ return output.Length;
+ }
+
+ public static int VerifySignature(SafeHandle securityContext, byte[] buffer, int offset, int count, out int newOffset, uint sequenceNumber)
+ {
+ if (securityContext is SafeDeleteGssContext)
+ {
+ // Sequence number is not used by libgssapi
+ newOffset = offset;
+ return Interop.GssApi.Decrypt(((SafeDeleteGssContext)securityContext).GssContext, buffer, offset, count);
+ }
+ newOffset = offset + 16;
+ count -= 16;
+ byte[] signature = ((SafeDeleteNtlmContext) securityContext).MakeSignature(false, buffer, newOffset, count);
+ for (int i = 0; i < signature.Length; i++)
+ {
+ if (buffer[offset + i] != signature[i]) throw new Exception("Invalid signature");
+ }
+ return count;
+ }
+
+ public static object QueryContextAttributes(SafeDeleteGssContext context, uint attribute, out SecurityStatusPal errorCode)
+ {
+ errorCode = SecurityStatusPal.OK;
+ switch (attribute)
+ {
+ case 0x01: // Names
+ return Interop.GssApi.GetSourceName(context.GssContext);
+ case 0x0C: // NegotiationInfo
+ //NegotiationInfoClass negotiationInfoClass = new NegotiationInfoClass(context, Int32.MaxValue);
+ //negotiationInfoClass.AuthenticationPackage = NegotiationInfoClass.Kerberos;
+ //return negotiationInfoClass;
+ case 0: // Sizes
+ // Used only in the Encrypt/Decrypt logic
+ case 0x1B: // ClientSpecifiedSpn
+ // Required only in NTLM case with ExtendedProtection
+ default:
+ errorCode = SecurityStatusPal.Unsupported;
+ return null;
+ }
+ }
+
+ private static bool IsNtlmClient(string targetName, SafeHandle credential)
+ {
+ return string.IsNullOrEmpty(targetName) || (credential is SafeFreeNtlmCredentials);
+ }
+
+ private static SecurityStatusPal InitializeNtlmSecurityContext(
+ SafeFreeNtlmCredentials credential,
+ ref SafeHandle context,
+ uint inFlags,
+ SecurityBuffer inputBuffer,
+ SecurityBuffer outputBuffer)
+ {
+ SecurityStatusPal retVal;
+
+ if (null == context)
+ {
+ context = new SafeDeleteNtlmContext(credential, inFlags);
+ outputBuffer.token = Interop.HeimdalNtlm.CreateNegotiateMessage(inFlags);
+ retVal = SecurityStatusPal.ContinueNeeded;
+ }
+ else
+ {
+ uint flags = ((SafeDeleteNtlmContext)context).Flags;
+ SafeNtlmBufferHandle sessionKey;
+ outputBuffer.token = Interop.HeimdalNtlm.CreateAuthenticateMessage(flags, credential.UserName,
+ credential.Password, credential.Domain, inputBuffer.token, inputBuffer.offset, inputBuffer.size, out sessionKey);
+ using (sessionKey)
+ {
+ ((SafeDeleteNtlmContext)context).SetKeys(sessionKey);
+ }
+ retVal = SecurityStatusPal.OK;
+ }
+ outputBuffer.size = outputBuffer.token.Length;
+ return retVal;
+ }
+
+ private static SecurityStatusPal EstablishSecurityContext(
+ SafeFreeGssCredentials credential,
+ ref SafeHandle context,
+ string targetName,
+ uint inFlags,
+ SecurityBuffer inputBuffer,
+ SecurityBuffer outputBuffer,
+ ref uint outFlags)
+ {
+ if (context == null)
+ {
+ context = new SafeDeleteGssContext(targetName, inFlags);
+ }
+
+ SafeDeleteGssContext gssContext = (SafeDeleteGssContext) context;
+ try
+ {
+ SafeGssContextHandle contextHandle = gssContext.GssContext;
+ bool done = Interop.GssApi.EstablishSecurityContext(
+ ref contextHandle,
+ credential,
+ gssContext.TargetName,
+ inFlags,
+ inputBuffer.token,
+ out outputBuffer.token,
+ out outFlags);
+
+ Debug.Assert(outputBuffer.token != null, "Unexpected null buffer returned by GssApi");
+ outputBuffer.size = outputBuffer.token.Length;
+ outputBuffer.offset = 0;
+
+ // Save the inner context handle for further calls to libgssapi
+ if (gssContext.IsInvalid)
+ {
+ gssContext.SetHandle(credential, contextHandle);
+ }
+ return done ? SecurityStatusPal.OK : SecurityStatusPal.ContinueNeeded;
+ }
+ catch (Exception)
+ {
+ return SecurityStatusPal.InternalError;
+ }
+ }
+ }
+}