Windows Authentication: Improved implementation

This commit is contained in:
Tal Aloni 2017-02-13 12:23:39 +02:00
parent acea85ca8c
commit d7e33e465d
2 changed files with 78 additions and 65 deletions

View file

@ -147,7 +147,7 @@ namespace SMBLibrary.Authentication.Win32
); );
[DllImport("Secur32.dll")] [DllImport("Secur32.dll")]
private extern static int DeleteSecurityContext( public extern static int DeleteSecurityContext(
ref SecHandle phContext ref SecHandle phContext
); );
@ -205,13 +205,14 @@ namespace SMBLibrary.Authentication.Win32
public static byte[] GetType1Message(string domainName, string userName, string password, out SecHandle clientContext) public static byte[] GetType1Message(string domainName, string userName, string password, out SecHandle clientContext)
{ {
SecHandle handle = AcquireNTLMCredentialsHandle(domainName, userName, password); SecHandle credentialsHandle = AcquireNTLMCredentialsHandle(domainName, userName, password);
clientContext = new SecHandle(); clientContext = new SecHandle();
SecBufferDesc output = new SecBufferDesc(MAX_TOKEN_SIZE); SecBuffer outputBuffer = new SecBuffer(MAX_TOKEN_SIZE);
SecBufferDesc output = new SecBufferDesc(outputBuffer);
uint contextAttributes; uint contextAttributes;
SECURITY_INTEGER expiry; SECURITY_INTEGER expiry;
int result = InitializeSecurityContext(ref handle, IntPtr.Zero, null, ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY, 0, SECURITY_NATIVE_DREP, IntPtr.Zero, 0, ref clientContext, ref output, out contextAttributes, out expiry); int result = InitializeSecurityContext(ref credentialsHandle, IntPtr.Zero, null, ISC_REQ_CONFIDENTIALITY | ISC_REQ_INTEGRITY, 0, SECURITY_NATIVE_DREP, IntPtr.Zero, 0, ref clientContext, ref output, out contextAttributes, out expiry);
if (result != SEC_E_OK && result != SEC_I_CONTINUE_NEEDED) if (result != SEC_E_OK && result != SEC_I_CONTINUE_NEEDED)
{ {
if ((uint)result == SEC_E_INVALID_HANDLE) if ((uint)result == SEC_E_INVALID_HANDLE)
@ -227,14 +228,20 @@ namespace SMBLibrary.Authentication.Win32
throw new Exception("InitializeSecurityContext failed, Error code " + ((uint)result).ToString("X")); throw new Exception("InitializeSecurityContext failed, Error code " + ((uint)result).ToString("X"));
} }
} }
return output.GetSecBufferBytes(); FreeCredentialsHandle(ref credentialsHandle);
byte[] messageBytes = outputBuffer.GetBufferBytes();
outputBuffer.Dispose();
output.Dispose();
return messageBytes;
} }
public static byte[] GetType3Message(SecHandle clientContext, byte[] type2Message) public static byte[] GetType3Message(SecHandle clientContext, byte[] type2Message)
{ {
SecHandle newContext = new SecHandle(); SecHandle newContext = new SecHandle();
SecBufferDesc input = new SecBufferDesc(type2Message); SecBuffer inputBuffer = new SecBuffer(type2Message);
SecBufferDesc output = new SecBufferDesc(MAX_TOKEN_SIZE); SecBufferDesc input = new SecBufferDesc(inputBuffer);
SecBuffer outputBuffer = new SecBuffer(MAX_TOKEN_SIZE);
SecBufferDesc output = new SecBufferDesc(outputBuffer);
uint contextAttributes; uint contextAttributes;
SECURITY_INTEGER expiry; SECURITY_INTEGER expiry;
@ -254,19 +261,26 @@ namespace SMBLibrary.Authentication.Win32
throw new Exception("InitializeSecurityContext failed, error code " + ((uint)result).ToString("X")); throw new Exception("InitializeSecurityContext failed, error code " + ((uint)result).ToString("X"));
} }
} }
return output.GetSecBufferBytes(); byte[] messageBytes = outputBuffer.GetBufferBytes();
inputBuffer.Dispose();
input.Dispose();
outputBuffer.Dispose();
output.Dispose();
return messageBytes;
} }
public static byte[] GetType2Message(byte[] type1MessageBytes, out SecHandle serverContext) public static byte[] GetType2Message(byte[] type1MessageBytes, out SecHandle serverContext)
{ {
SecHandle handle = AcquireNTLMCredentialsHandle(); SecHandle credentialsHandle = AcquireNTLMCredentialsHandle();
SecBufferDesc type1Message = new SecBufferDesc(type1MessageBytes); SecBuffer inputBuffer = new SecBuffer(type1MessageBytes);
SecBufferDesc input = new SecBufferDesc(inputBuffer);
serverContext = new SecHandle(); serverContext = new SecHandle();
SecBufferDesc output = new SecBufferDesc(MAX_TOKEN_SIZE); SecBuffer outputBuffer = new SecBuffer(MAX_TOKEN_SIZE);
SecBufferDesc output = new SecBufferDesc(outputBuffer);
uint contextAttributes; uint contextAttributes;
SECURITY_INTEGER timestamp; SECURITY_INTEGER timestamp;
int result = AcceptSecurityContext(ref handle, IntPtr.Zero, ref type1Message, ASC_REQ_INTEGRITY | ASC_REQ_CONFIDENTIALITY, SECURITY_NATIVE_DREP, ref serverContext, ref output, out contextAttributes, out timestamp); int result = AcceptSecurityContext(ref credentialsHandle, IntPtr.Zero, ref input, ASC_REQ_INTEGRITY | ASC_REQ_CONFIDENTIALITY, SECURITY_NATIVE_DREP, ref serverContext, ref output, out contextAttributes, out timestamp);
if (result != SEC_E_OK && result != SEC_I_CONTINUE_NEEDED) if (result != SEC_E_OK && result != SEC_I_CONTINUE_NEEDED)
{ {
if ((uint)result == SEC_E_INVALID_HANDLE) if ((uint)result == SEC_E_INVALID_HANDLE)
@ -282,8 +296,13 @@ namespace SMBLibrary.Authentication.Win32
throw new Exception("AcceptSecurityContext failed, error code " + ((uint)result).ToString("X")); throw new Exception("AcceptSecurityContext failed, error code " + ((uint)result).ToString("X"));
} }
} }
FreeCredentialsHandle(ref handle); FreeCredentialsHandle(ref credentialsHandle);
return output.GetSecBufferBytes(); byte[] messageBytes = outputBuffer.GetBufferBytes();
inputBuffer.Dispose();
input.Dispose();
outputBuffer.Dispose();
output.Dispose();
return messageBytes;
} }
/// <summary> /// <summary>
@ -303,12 +322,19 @@ namespace SMBLibrary.Authentication.Win32
public static bool AuthenticateType3Message(SecHandle serverContext, byte[] type3MessageBytes) public static bool AuthenticateType3Message(SecHandle serverContext, byte[] type3MessageBytes)
{ {
SecHandle newContext = new SecHandle(); SecHandle newContext = new SecHandle();
SecBufferDesc type3Message = new SecBufferDesc(type3MessageBytes); SecBuffer inputBuffer = new SecBuffer(type3MessageBytes);
SecBufferDesc output = new SecBufferDesc(MAX_TOKEN_SIZE); SecBufferDesc input = new SecBufferDesc(inputBuffer);
SecBuffer outputBuffer = new SecBuffer(MAX_TOKEN_SIZE);
SecBufferDesc output = new SecBufferDesc(outputBuffer);
uint contextAttributes; uint contextAttributes;
SECURITY_INTEGER timestamp; SECURITY_INTEGER timestamp;
int result = AcceptSecurityContext(IntPtr.Zero, ref serverContext, ref type3Message, ASC_REQ_INTEGRITY | ASC_REQ_CONFIDENTIALITY, SECURITY_NATIVE_DREP, ref newContext, ref output, out contextAttributes, out timestamp); int result = AcceptSecurityContext(IntPtr.Zero, ref serverContext, ref input, ASC_REQ_INTEGRITY | ASC_REQ_CONFIDENTIALITY, SECURITY_NATIVE_DREP, ref newContext, ref output, out contextAttributes, out timestamp);
inputBuffer.Dispose();
input.Dispose();
outputBuffer.Dispose();
output.Dispose();
if (result == SEC_E_OK) if (result == SEC_E_OK)
{ {

View file

@ -1,4 +1,4 @@
/* Copyright (C) 2014 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved. /* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
* *
* You can redistribute this program and/or modify it under the terms of * You can redistribute this program and/or modify it under the terms of
* the GNU Lesser Public License as published by the Free Software Foundation, * the GNU Lesser Public License as published by the Free Software Foundation,
@ -11,7 +11,7 @@ using System.Text;
namespace SMBLibrary.Authentication.Win32 namespace SMBLibrary.Authentication.Win32
{ {
public enum SecBufferType public enum SecBufferType : uint
{ {
SECBUFFER_VERSION = 0, SECBUFFER_VERSION = 0,
SECBUFFER_EMPTY = 0, SECBUFFER_EMPTY = 0,
@ -20,33 +20,33 @@ namespace SMBLibrary.Authentication.Win32
} }
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct SecBuffer public struct SecBuffer : IDisposable
{ {
public int cbBuffer; public uint cbBuffer; // Specifies the size, in bytes, of the buffer pointed to by the pvBuffer member.
public int BufferType; public uint BufferType;
public IntPtr pvBuffer; public IntPtr pvBuffer; // A pointer to a buffer.
public SecBuffer(int bufferSize) public SecBuffer(int bufferSize)
{ {
cbBuffer = bufferSize; cbBuffer = (uint)bufferSize;
BufferType = (int)SecBufferType.SECBUFFER_TOKEN; BufferType = (uint)SecBufferType.SECBUFFER_TOKEN;
pvBuffer = Marshal.AllocHGlobal(bufferSize); pvBuffer = Marshal.AllocHGlobal(bufferSize);
} }
public SecBuffer(byte[] secBufferBytes) public SecBuffer(byte[] secBufferBytes)
{ {
cbBuffer = secBufferBytes.Length; cbBuffer = (uint)secBufferBytes.Length;
BufferType = (int)SecBufferType.SECBUFFER_TOKEN; BufferType = (uint)SecBufferType.SECBUFFER_TOKEN;
pvBuffer = Marshal.AllocHGlobal(cbBuffer); pvBuffer = Marshal.AllocHGlobal(secBufferBytes.Length);
Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); Marshal.Copy(secBufferBytes, 0, pvBuffer, secBufferBytes.Length);
} }
public SecBuffer(byte[] secBufferBytes, SecBufferType bufferType) public SecBuffer(byte[] secBufferBytes, SecBufferType bufferType)
{ {
cbBuffer = secBufferBytes.Length; cbBuffer = (uint)secBufferBytes.Length;
BufferType = (int)bufferType; BufferType = (uint)bufferType;
pvBuffer = Marshal.AllocHGlobal(cbBuffer); pvBuffer = Marshal.AllocHGlobal(secBufferBytes.Length);
Marshal.Copy(secBufferBytes, 0, pvBuffer, cbBuffer); Marshal.Copy(secBufferBytes, 0, pvBuffer, secBufferBytes.Length);
} }
public void Dispose() public void Dispose()
@ -58,63 +58,50 @@ namespace SMBLibrary.Authentication.Win32
} }
} }
public byte[] GetBytes() public byte[] GetBufferBytes()
{ {
byte[] buffer = null; byte[] buffer = null;
if (cbBuffer > 0) if (cbBuffer > 0)
{ {
buffer = new byte[cbBuffer]; buffer = new byte[cbBuffer];
Marshal.Copy(pvBuffer, buffer, 0, cbBuffer); Marshal.Copy(pvBuffer, buffer, 0, (int)cbBuffer);
} }
return buffer; return buffer;
} }
} }
/// <summary>
/// Simplified SecBufferDesc struct with only one SecBuffer
/// </summary>
[StructLayout(LayoutKind.Sequential)] [StructLayout(LayoutKind.Sequential)]
public struct SecBufferDesc public struct SecBufferDesc : IDisposable
{ {
public int ulVersion; public uint ulVersion;
public int cBuffers; public uint cBuffers; // Indicates the number of SecBuffer structures in the pBuffers array.
public IntPtr pBuffers; public IntPtr pBuffers; // Pointer to an array of SecBuffer structures.
public SecBufferDesc(int bufferSize) public SecBufferDesc(SecBuffer buffer) : this(new SecBuffer[] { buffer })
{ {
ulVersion = (int)SecBufferType.SECBUFFER_VERSION;
cBuffers = 1;
SecBuffer secBuffer = new SecBuffer(bufferSize);
pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(secBuffer));
Marshal.StructureToPtr(secBuffer, pBuffers, false);
} }
public SecBufferDesc(byte[] secBufferBytes) public SecBufferDesc(SecBuffer[] buffers)
{ {
ulVersion = (int)SecBufferType.SECBUFFER_VERSION; int secBufferSize = Marshal.SizeOf(typeof(SecBuffer));
cBuffers = 1; ulVersion = (uint)SecBufferType.SECBUFFER_VERSION;
SecBuffer secBuffer = new SecBuffer(secBufferBytes); cBuffers = (uint)buffers.Length;
pBuffers = Marshal.AllocHGlobal(Marshal.SizeOf(secBuffer)); pBuffers = Marshal.AllocHGlobal(buffers.Length * secBufferSize);
Marshal.StructureToPtr(secBuffer, pBuffers, false); IntPtr currentBuffer = pBuffers;
for (int index = 0; index < buffers.Length; index++)
{
Marshal.StructureToPtr(buffers[index], currentBuffer, false);
currentBuffer = new IntPtr(currentBuffer.ToInt64() + secBufferSize);
}
} }
public void Dispose() public void Dispose()
{ {
if (pBuffers != IntPtr.Zero) if (pBuffers != IntPtr.Zero)
{ {
SecBuffer secBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer));
secBuffer.Dispose();
Marshal.FreeHGlobal(pBuffers); Marshal.FreeHGlobal(pBuffers);
pBuffers = IntPtr.Zero; pBuffers = IntPtr.Zero;
} }
} }
public byte[] GetSecBufferBytes()
{
if (pBuffers == IntPtr.Zero)
throw new ObjectDisposedException("SecBufferDesc");
SecBuffer secBuffer = (SecBuffer)Marshal.PtrToStructure(pBuffers, typeof(SecBuffer));
return secBuffer.GetBytes();
}
} }
} }