Added support for GSSAPI tokens

This commit is contained in:
Tal Aloni 2017-01-08 00:11:15 +02:00
parent aaef6f2fc3
commit cf9aba4a31
11 changed files with 658 additions and 20 deletions

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,
@ -16,6 +16,8 @@ namespace SMBLibrary.Authentication
/// </summary> /// </summary>
public class AuthenticateMessage public class AuthenticateMessage
{ {
public const string ValidSignature = "NTLMSSP\0";
public string Signature; // 8 bytes public string Signature; // 8 bytes
public MessageTypeName MessageType; public MessageTypeName MessageType;
public byte[] LmChallengeResponse; public byte[] LmChallengeResponse;
@ -30,7 +32,7 @@ namespace SMBLibrary.Authentication
public AuthenticateMessage() public AuthenticateMessage()
{ {
Signature = AuthenticationMessageUtils.ValidSignature; Signature = ValidSignature;
MessageType = MessageTypeName.Authenticate; MessageType = MessageTypeName.Authenticate;
DomainName = String.Empty; DomainName = String.Empty;
UserName = String.Empty; UserName = String.Empty;
@ -66,7 +68,7 @@ namespace SMBLibrary.Authentication
int payloadLength = LmChallengeResponse.Length + NtChallengeResponse.Length + DomainName.Length * 2 + UserName.Length * 2 + WorkStation.Length * 2 + EncryptedRandomSessionKey.Length; int payloadLength = LmChallengeResponse.Length + NtChallengeResponse.Length + DomainName.Length * 2 + UserName.Length * 2 + WorkStation.Length * 2 + EncryptedRandomSessionKey.Length;
byte[] buffer = new byte[fixedLength + payloadLength]; byte[] buffer = new byte[fixedLength + payloadLength];
ByteWriter.WriteAnsiString(buffer, 0, AuthenticationMessageUtils.ValidSignature, 8); ByteWriter.WriteAnsiString(buffer, 0, ValidSignature, 8);
LittleEndianWriter.WriteUInt32(buffer, 8, (uint)MessageType); LittleEndianWriter.WriteUInt32(buffer, 8, (uint)MessageType);
LittleEndianWriter.WriteUInt32(buffer, 60, (uint)NegotiateFlags); LittleEndianWriter.WriteUInt32(buffer, 60, (uint)NegotiateFlags);
if ((NegotiateFlags & NegotiateFlags.NegotiateVersion) > 0) if ((NegotiateFlags & NegotiateFlags.NegotiateVersion) > 0)

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,
@ -13,8 +13,6 @@ namespace SMBLibrary.Authentication
{ {
public class AuthenticationMessageUtils public class AuthenticationMessageUtils
{ {
public const string ValidSignature = "NTLMSSP\0";
public static string ReadAnsiStringBufferPointer(byte[] buffer, int offset) public static string ReadAnsiStringBufferPointer(byte[] buffer, int offset)
{ {
byte[] bytes = ReadBufferPointer(buffer, offset); byte[] bytes = ReadBufferPointer(buffer, offset);
@ -52,8 +50,12 @@ namespace SMBLibrary.Authentication
public static bool IsSignatureValid(byte[] messageBytes) public static bool IsSignatureValid(byte[] messageBytes)
{ {
if (messageBytes.Length < 8)
{
return false;
}
string signature = ByteReader.ReadAnsiString(messageBytes, 0, 8); string signature = ByteReader.ReadAnsiString(messageBytes, 0, 8);
return (signature == ValidSignature); return (signature == AuthenticateMessage.ValidSignature);
} }
public static MessageTypeName GetMessageType(byte[] messageBytes) public static MessageTypeName GetMessageType(byte[] messageBytes)

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,
@ -27,7 +27,7 @@ namespace SMBLibrary.Authentication
public ChallengeMessage() public ChallengeMessage()
{ {
Signature = AuthenticationMessageUtils.ValidSignature; Signature = AuthenticateMessage.ValidSignature;
MessageType = MessageTypeName.Challenge; MessageType = MessageTypeName.Challenge;
} }
@ -56,7 +56,7 @@ namespace SMBLibrary.Authentication
int payloadLength = TargetName.Length * 2 + TargetInfo.Length; int payloadLength = TargetName.Length * 2 + TargetInfo.Length;
byte[] buffer = new byte[fixedLength + payloadLength]; byte[] buffer = new byte[fixedLength + payloadLength];
ByteWriter.WriteAnsiString(buffer, 0, AuthenticationMessageUtils.ValidSignature, 8); ByteWriter.WriteAnsiString(buffer, 0, AuthenticateMessage.ValidSignature, 8);
LittleEndianWriter.WriteUInt32(buffer, 8, (uint)MessageType); LittleEndianWriter.WriteUInt32(buffer, 8, (uint)MessageType);
LittleEndianWriter.WriteUInt32(buffer, 20, (uint)NegotiateFlags); LittleEndianWriter.WriteUInt32(buffer, 20, (uint)NegotiateFlags);
ByteWriter.WriteBytes(buffer, 24, ServerChallenge); ByteWriter.WriteBytes(buffer, 24, ServerChallenge);

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,
@ -25,7 +25,7 @@ namespace SMBLibrary.Authentication
public NegotiateMessage() public NegotiateMessage()
{ {
Signature = AuthenticationMessageUtils.ValidSignature; Signature = AuthenticateMessage.ValidSignature;
MessageType = MessageTypeName.Negotiate; MessageType = MessageTypeName.Negotiate;
DomainName = String.Empty; DomainName = String.Empty;
Workstation = String.Empty; Workstation = String.Empty;
@ -53,7 +53,7 @@ namespace SMBLibrary.Authentication
} }
int payloadLength = DomainName.Length * 2 + Workstation.Length * 2; int payloadLength = DomainName.Length * 2 + Workstation.Length * 2;
byte[] buffer = new byte[fixedLength + payloadLength]; byte[] buffer = new byte[fixedLength + payloadLength];
ByteWriter.WriteAnsiString(buffer, 0, AuthenticationMessageUtils.ValidSignature, 8); ByteWriter.WriteAnsiString(buffer, 0, AuthenticateMessage.ValidSignature, 8);
LittleEndianWriter.WriteUInt32(buffer, 8, (uint)MessageType); LittleEndianWriter.WriteUInt32(buffer, 8, (uint)MessageType);
LittleEndianWriter.WriteUInt32(buffer, 12, (uint)NegotiateFlags); LittleEndianWriter.WriteUInt32(buffer, 12, (uint)NegotiateFlags);

View file

@ -0,0 +1,82 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* 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,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using Utilities;
namespace SMBLibrary.Authentication
{
public enum DerEncodingTag : byte
{
ByteArray = 0x04,
ObjectIdentifier = 0x06,
Enum = 0x0A,
Sequence = 0x30,
}
public class DerEncodingHelper
{
public static int ReadLength(byte[] buffer, ref int offset)
{
int length = ByteReader.ReadByte(buffer, ref offset);
if (length >= 0x80)
{
int lengthFieldSize = (length & 0x7F);
byte[] lengthField = ByteReader.ReadBytes(buffer, ref offset, lengthFieldSize);
length = 0;
foreach (byte value in lengthField)
{
length *= Byte.MaxValue;
length += value;
}
}
return length;
}
public static void WriteLength(byte[] buffer, ref int offset, int length)
{
if (length >= 0x80)
{
List<byte> values = new List<byte>();
do
{
byte value = (byte)(length % Byte.MaxValue);
values.Add(value);
length = value / Byte.MaxValue;
}
while (length > 0);
values.Reverse();
byte[] lengthField = values.ToArray();
ByteWriter.WriteByte(buffer, ref offset, (byte)(0x80 | lengthField.Length));
ByteWriter.WriteBytes(buffer, ref offset, lengthField);
}
else
{
ByteWriter.WriteByte(buffer, ref offset, (byte)length);
}
}
public static int GetLengthFieldSize(int length)
{
if (length >= 0x80)
{
int result = 1;
do
{
length = length / Byte.MaxValue;
result++;
}
while(length > 0);
return result;
}
else
{
return 1;
}
}
}
}

View file

@ -0,0 +1,62 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* 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,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using Utilities;
namespace SMBLibrary.Authentication
{
public class GSSAPIHelper
{
public static readonly byte[] NTLMSSPIdentifier = new byte[] { 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a };
/// <summary>
/// https://msdn.microsoft.com/en-us/library/ms995330.aspx
/// </summary>
public static byte[] GetNTLMSSPMessage(byte[] tokenBytes)
{
SimpleProtectedNegotiationToken token = SimpleProtectedNegotiationToken.ReadToken(tokenBytes, 0);
if (token != null)
{
if (token is SimpleProtectedNegotiationTokenInit)
{
List<TokenInitEntry> tokens = ((SimpleProtectedNegotiationTokenInit)token).Tokens;
foreach (TokenInitEntry entry in tokens)
{
foreach (byte[] identifier in entry.MechanismTypeList)
{
if (ByteUtils.AreByteArraysEqual(identifier, NTLMSSPIdentifier))
{
return entry.MechanismToken;
}
}
}
}
else
{
List<TokenResponseEntry> tokens = ((SimpleProtectedNegotiationTokenResponse)token).Tokens;
if (tokens.Count > 0)
{
return tokens[0].ResponseToken;
}
}
}
return null;
}
public static byte[] GetGSSTokenResponseBytesFromNTLMSSPMessage(byte[] messageBytes)
{
SimpleProtectedNegotiationTokenResponse token = new SimpleProtectedNegotiationTokenResponse();
TokenResponseEntry entry = new TokenResponseEntry();
entry.NegState = NegState.AcceptIncomplete;
entry.SupportedMechanism = NTLMSSPIdentifier;
entry.ResponseToken = messageBytes;
token.Tokens.Add(entry);
return token.GetBytes();
}
}
}

View file

@ -0,0 +1,52 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* 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,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.Text;
using Utilities;
namespace SMBLibrary.Authentication
{
public abstract class SimpleProtectedNegotiationToken
{
public const byte ApplicationTag = 0x60;
public static readonly byte[] SPNEGOIdentifier = new byte[] { 0x2b, 0x06, 0x01, 0x05, 0x05, 0x02 };
public abstract byte[] GetBytes();
/// <summary>
/// https://tools.ietf.org/html/rfc2743
/// </summary>
public static SimpleProtectedNegotiationToken ReadToken(byte[] tokenBytes, int offset)
{
byte tag = ByteReader.ReadByte(tokenBytes, ref offset);
if (tag == ApplicationTag)
{
// when an InitToken is sent, it is prepended by an Application Constructed Object specifier (0x60),
// and the OID for SPNEGO (see value in OID table above). This is the generic GSSAPI header.
int tokenLength = DerEncodingHelper.ReadLength(tokenBytes, ref offset);
tag = ByteReader.ReadByte(tokenBytes, ref offset);
if (tag == (byte)DerEncodingTag.ObjectIdentifier)
{
int objectIdentifierLength = DerEncodingHelper.ReadLength(tokenBytes, ref offset);
byte[] objectIdentifier = ByteReader.ReadBytes(tokenBytes, ref offset, objectIdentifierLength);
if (ByteUtils.AreByteArraysEqual(objectIdentifier, SPNEGOIdentifier))
{
return new SimpleProtectedNegotiationTokenInit(tokenBytes, offset);
}
}
}
else if (tag == SimpleProtectedNegotiationTokenResponse.NegTokenRespTag)
{
offset--;
return new SimpleProtectedNegotiationTokenResponse(tokenBytes, offset);
}
return null;
}
}
}

View file

@ -0,0 +1,204 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* 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,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.IO;
using Utilities;
namespace SMBLibrary.Authentication
{
public class TokenInitEntry
{
public List<byte[]> MechanismTypeList = new List<byte[]>(); // Optional
// reqFlags - Optional, Unused
public byte[] MechanismToken = new byte[0]; // Optional
// mechListMIC - Optional, Unused
}
/// <summary>
/// RFC 4178 - negTokenInit
/// </summary>
public class SimpleProtectedNegotiationTokenInit : SimpleProtectedNegotiationToken
{
public const byte NegTokenInitTag = 0xA0;
public const byte MechanismTypeListTag = 0xA0;
public const byte RequiredFlagsTag = 0xA1;
public const byte MechanismTokenTag = 0xA2;
public const byte MechanismListMICTag = 0xA3;
public List<TokenInitEntry> Tokens = new List<TokenInitEntry>();
public SimpleProtectedNegotiationTokenInit(byte[] buffer, int offset)
{
byte tag = ByteReader.ReadByte(buffer, ref offset);
if (tag != NegTokenInitTag)
{
throw new InvalidDataException();
}
int constructionLength = DerEncodingHelper.ReadLength(buffer, ref offset);
int sequenceEndOffset = offset + constructionLength;
tag = ByteReader.ReadByte(buffer, ref offset);
if (tag != (byte)DerEncodingTag.Sequence)
{
throw new InvalidDataException();
}
while (offset < sequenceEndOffset)
{
int entryLength = DerEncodingHelper.ReadLength(buffer, ref offset);
int entryEndOffset = offset + entryLength;
TokenInitEntry entry = new TokenInitEntry();
while (offset < entryEndOffset)
{
tag = ByteReader.ReadByte(buffer, ref offset);
if (tag == MechanismTypeListTag)
{
entry.MechanismTypeList = ReadMechanismTypeList(buffer, ref offset);
}
else if (tag == MechanismTokenTag)
{
entry.MechanismToken = ReadMechanismToken(buffer, ref offset);
}
else
{
throw new InvalidDataException();
}
}
Tokens.Add(entry);
}
}
public override byte[] GetBytes()
{
int sequenceLength = 0;
foreach (TokenInitEntry token in Tokens)
{
int entryLength = GetEntryLength(token);
sequenceLength += DerEncodingHelper.GetLengthFieldSize(entryLength) + entryLength;
}
int constructionLengthFieldSize = DerEncodingHelper.GetLengthFieldSize(1 + sequenceLength);
int bufferSize = 1 + constructionLengthFieldSize + 1 + sequenceLength;
byte[] buffer = new byte[bufferSize];
int offset = 0;
ByteWriter.WriteByte(buffer, ref offset, NegTokenInitTag);
DerEncodingHelper.WriteLength(buffer, ref offset, 1 + sequenceLength);
ByteWriter.WriteByte(buffer, ref offset, (byte)DerEncodingTag.Sequence);
foreach (TokenInitEntry token in Tokens)
{
int entryLength = GetEntryLength(token);
DerEncodingHelper.WriteLength(buffer, ref offset, entryLength);
if (token.MechanismTypeList != null)
{
WriteMechanismTypeList(buffer, ref offset, token.MechanismTypeList);
}
if (token.MechanismToken != null)
{
WriteMechanismToken(buffer, ref offset, token.MechanismToken);
}
}
return buffer;
}
public int GetEntryLength(TokenInitEntry token)
{
int result = 0;
if (token.MechanismTypeList != null)
{
int typeListSequenceLength = GetSequenceLength(token.MechanismTypeList);
int constructionLenthFieldSize = DerEncodingHelper.GetLengthFieldSize(1 + typeListSequenceLength);
int typeListLength = 1 + constructionLenthFieldSize + 1 + typeListSequenceLength;
result += typeListLength;
}
if (token.MechanismToken != null)
{
int byteArrayFieldSize = DerEncodingHelper.GetLengthFieldSize(token.MechanismToken.Length);
int constructionLengthFieldSize = DerEncodingHelper.GetLengthFieldSize(1 + byteArrayFieldSize + token.MechanismToken.Length);
int tokenLength = 1 + constructionLengthFieldSize + 1 + byteArrayFieldSize + token.MechanismToken.Length;
result += tokenLength;
}
return result;
}
private static List<byte[]> ReadMechanismTypeList(byte[] buffer, ref int offset)
{
List<byte[]> result = new List<byte[]>();
int constructionLength = DerEncodingHelper.ReadLength(buffer, ref offset);
int sequenceEndOffset = offset + constructionLength;
byte tag = ByteReader.ReadByte(buffer, ref offset);
if (tag != (byte)DerEncodingTag.Sequence)
{
throw new InvalidDataException();
}
while (offset < sequenceEndOffset)
{
int entryLength = DerEncodingHelper.ReadLength(buffer, ref offset);
int entryEndOffset = offset + entryLength;
tag = ByteReader.ReadByte(buffer, ref offset);
if (tag != (byte)DerEncodingTag.ObjectIdentifier)
{
throw new InvalidDataException();
}
int mechanismTypeLength = DerEncodingHelper.ReadLength(buffer, ref offset);
byte[] mechanismType = ByteReader.ReadBytes(buffer, ref offset, mechanismTypeLength);
result.Add(mechanismType);
}
return result;
}
private static byte[] ReadMechanismToken(byte[] buffer, ref int offset)
{
int constructionLength = DerEncodingHelper.ReadLength(buffer, ref offset);
byte tag = ByteReader.ReadByte(buffer, ref offset);
if (tag != (byte)DerEncodingTag.ByteArray)
{
throw new InvalidDataException();
}
int mechanismTokenLength = DerEncodingHelper.ReadLength(buffer, ref offset);
byte[] token = ByteReader.ReadBytes(buffer, ref offset, mechanismTokenLength);
return token;
}
private static int GetSequenceLength(List<byte[]> mechanismTypeList)
{
int sequenceLength = 0;
foreach (byte[] mechanismType in mechanismTypeList)
{
int lengthFieldSize = DerEncodingHelper.GetLengthFieldSize(mechanismType.Length);
int entryLength = 1 + lengthFieldSize + mechanismType.Length;
sequenceLength += DerEncodingHelper.GetLengthFieldSize(entryLength) + entryLength;
}
return sequenceLength;
}
private static void WriteMechanismTypeList(byte[] buffer, ref int offset, List<byte[]> mechanismTypeList)
{
int sequenceLength = GetSequenceLength(mechanismTypeList);
ByteWriter.WriteByte(buffer, ref offset, MechanismTypeListTag);
DerEncodingHelper.WriteLength(buffer, ref offset, 1 + sequenceLength);
ByteWriter.WriteByte(buffer, ref offset, (byte)DerEncodingTag.Sequence);
foreach (byte[] mechanismType in mechanismTypeList)
{
int lengthFieldSize = DerEncodingHelper.GetLengthFieldSize(mechanismType.Length);
int entryLength = 1 + lengthFieldSize + mechanismType.Length;
DerEncodingHelper.WriteLength(buffer, ref offset, entryLength);
ByteWriter.WriteByte(buffer, ref offset, (byte)DerEncodingTag.ObjectIdentifier);
DerEncodingHelper.WriteLength(buffer, ref offset, mechanismType.Length);
ByteWriter.WriteBytes(buffer, ref offset, mechanismType);
}
}
private static void WriteMechanismToken(byte[] buffer, ref int offset, byte[] mechanismToken)
{
int constructionLength = 1 + DerEncodingHelper.GetLengthFieldSize(mechanismToken.Length) + mechanismToken.Length;
ByteWriter.WriteByte(buffer, ref offset, MechanismTokenTag);
DerEncodingHelper.WriteLength(buffer, ref offset, constructionLength);
ByteWriter.WriteByte(buffer, ref offset, (byte)DerEncodingTag.ByteArray);
DerEncodingHelper.WriteLength(buffer, ref offset, mechanismToken.Length);
ByteWriter.WriteBytes(buffer, ref offset, mechanismToken);
}
}
}

View file

@ -0,0 +1,215 @@
/* Copyright (C) 2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
*
* 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,
* either version 3 of the License, or (at your option) any later version.
*/
using System;
using System.Collections.Generic;
using System.IO;
using Utilities;
namespace SMBLibrary.Authentication
{
public enum NegState : byte
{
AcceptCompleted = 0x00,
AcceptIncomplete = 0x01,
Reject = 0x02,
RequestMic = 0x03,
}
public class TokenResponseEntry
{
public NegState? NegState; // Optional
public byte[] SupportedMechanism; // Optional
public byte[] ResponseToken; // Optional
// mechListMIC - Optional, Unused
}
/// <summary>
/// RFC 4178 - negTokenInit
/// </summary>
public class SimpleProtectedNegotiationTokenResponse : SimpleProtectedNegotiationToken
{
public const byte NegTokenRespTag = 0xA1;
public const byte NegStateTag = 0xA0;
public const byte SupportedMechanismTag = 0xA1;
public const byte ResponseTokenTag = 0xA2;
public const byte MechanismListMICTag = 0xA3;
public List<TokenResponseEntry> Tokens = new List<TokenResponseEntry>();
public SimpleProtectedNegotiationTokenResponse()
{
}
public SimpleProtectedNegotiationTokenResponse(byte[] buffer, int offset)
{
byte tag = ByteReader.ReadByte(buffer, ref offset);
if (tag != NegTokenRespTag)
{
throw new InvalidDataException();
}
int constuctionLength = DerEncodingHelper.ReadLength(buffer, ref offset);
int sequenceEndOffset = offset + constuctionLength;
tag = ByteReader.ReadByte(buffer, ref offset);
if (tag != (byte)DerEncodingTag.Sequence)
{
throw new InvalidDataException();
}
while (offset < sequenceEndOffset)
{
int entryLength = DerEncodingHelper.ReadLength(buffer, ref offset);
int entryEndOffset = offset + entryLength;
TokenResponseEntry entry = new TokenResponseEntry();
while (offset < entryEndOffset)
{
tag = ByteReader.ReadByte(buffer, ref offset);
if (tag == NegStateTag)
{
entry.NegState = ReadNegState(buffer, ref offset);
}
else if (tag == SupportedMechanismTag)
{
entry.SupportedMechanism = ReadSupportedMechanism(buffer, ref offset);
}
else if (tag == ResponseTokenTag)
{
entry.ResponseToken = ReadResponseToken(buffer, ref offset);
}
else
{
throw new InvalidDataException();
}
}
Tokens.Add(entry);
}
}
public override byte[] GetBytes()
{
int sequenceLength = 0;
foreach (TokenResponseEntry token in Tokens)
{
int entryLength = GetEntryLength(token);
sequenceLength += DerEncodingHelper.GetLengthFieldSize(entryLength) + entryLength;
}
int constructionLengthFieldSize = DerEncodingHelper.GetLengthFieldSize(1 + sequenceLength);
int bufferSize = 1 + constructionLengthFieldSize + 1 + sequenceLength;
byte[] buffer = new byte[bufferSize];
int offset = 0;
ByteWriter.WriteByte(buffer, ref offset, NegTokenRespTag);
DerEncodingHelper.WriteLength(buffer, ref offset, 1 + sequenceLength);
ByteWriter.WriteByte(buffer, ref offset, (byte)DerEncodingTag.Sequence);
foreach (TokenResponseEntry token in Tokens)
{
int entryLength = GetEntryLength(token);
DerEncodingHelper.WriteLength(buffer, ref offset, entryLength);
if (token.NegState.HasValue)
{
WriteNegState(buffer, ref offset, token.NegState.Value);
}
if (token.SupportedMechanism != null)
{
WriteSupportedMechanism(buffer, ref offset, token.SupportedMechanism);
}
if (token.ResponseToken != null)
{
WriteResponseToken(buffer, ref offset, token.ResponseToken);
}
}
return buffer;
}
private static NegState ReadNegState(byte[] buffer, ref int offset)
{
int length = DerEncodingHelper.ReadLength(buffer, ref offset);
byte tag = ByteReader.ReadByte(buffer, ref offset);
if (tag != (byte)DerEncodingTag.Enum)
{
throw new InvalidDataException();
}
length = DerEncodingHelper.ReadLength(buffer, ref offset);
return (NegState)ByteReader.ReadByte(buffer, ref offset);
}
private static byte[] ReadSupportedMechanism(byte[] buffer, ref int offset)
{
int constructionLength = DerEncodingHelper.ReadLength(buffer, ref offset);
byte tag = ByteReader.ReadByte(buffer, ref offset);
if (tag != (byte)DerEncodingTag.ObjectIdentifier)
{
throw new InvalidDataException();
}
int length = DerEncodingHelper.ReadLength(buffer, ref offset);
return ByteReader.ReadBytes(buffer, ref offset, length);
}
private static byte[] ReadResponseToken(byte[] buffer, ref int offset)
{
int constructionLength = DerEncodingHelper.ReadLength(buffer, ref offset);
byte tag = ByteReader.ReadByte(buffer, ref offset);
if (tag != (byte)DerEncodingTag.ByteArray)
{
throw new InvalidDataException();
}
int length = DerEncodingHelper.ReadLength(buffer, ref offset);
return ByteReader.ReadBytes(buffer, ref offset, length);
}
private static int GetEntryLength(TokenResponseEntry token)
{
int result = 0;
if (token.NegState.HasValue)
{
int negStateLength = 5;
result += negStateLength;
}
if (token.SupportedMechanism != null)
{
int supportedMechanismLength2FieldSize = DerEncodingHelper.GetLengthFieldSize(token.SupportedMechanism.Length);
int supportedMechanismLength1FieldSize = DerEncodingHelper.GetLengthFieldSize(1 + supportedMechanismLength2FieldSize + token.SupportedMechanism.Length);
int supportedMechanismLength = 1 + supportedMechanismLength1FieldSize + 1 + supportedMechanismLength2FieldSize + token.SupportedMechanism.Length;
result += supportedMechanismLength;
}
if (token.ResponseToken != null)
{
int responseToken2FieldSize = DerEncodingHelper.GetLengthFieldSize(token.ResponseToken.Length);
int responseToken1FieldSize = DerEncodingHelper.GetLengthFieldSize(1 + responseToken2FieldSize + token.ResponseToken.Length);
int responseTokenLength = 1 + responseToken1FieldSize + 1 + responseToken2FieldSize + token.ResponseToken.Length;
result += responseTokenLength;
}
return result;
}
private static void WriteNegState(byte[] buffer, ref int offset, NegState negState)
{
ByteWriter.WriteByte(buffer, ref offset, NegStateTag);
DerEncodingHelper.WriteLength(buffer, ref offset, 3);
ByteWriter.WriteByte(buffer, ref offset, (byte)DerEncodingTag.Enum);
DerEncodingHelper.WriteLength(buffer, ref offset, 1);
ByteWriter.WriteByte(buffer, ref offset, (byte)negState);
}
private static void WriteSupportedMechanism(byte[] buffer, ref int offset, byte[] supportedMechanism)
{
int supportedMechanismLengthFieldSize = DerEncodingHelper.GetLengthFieldSize(supportedMechanism.Length);
ByteWriter.WriteByte(buffer, ref offset, SupportedMechanismTag);
DerEncodingHelper.WriteLength(buffer, ref offset, 1 + supportedMechanismLengthFieldSize + supportedMechanism.Length);
ByteWriter.WriteByte(buffer, ref offset, (byte)DerEncodingTag.ObjectIdentifier);
DerEncodingHelper.WriteLength(buffer, ref offset, supportedMechanism.Length);
ByteWriter.WriteBytes(buffer, ref offset, supportedMechanism);
}
private static void WriteResponseToken(byte[] buffer, ref int offset, byte[] responseToken)
{
int responseTokenLengthFieldSize = DerEncodingHelper.GetLengthFieldSize(responseToken.Length);
ByteWriter.WriteByte(buffer, ref offset, ResponseTokenTag);
DerEncodingHelper.WriteLength(buffer, ref offset, 1 + responseTokenLengthFieldSize + responseToken.Length);
ByteWriter.WriteByte(buffer, ref offset, (byte)DerEncodingTag.ByteArray);
DerEncodingHelper.WriteLength(buffer, ref offset, responseToken.Length);
ByteWriter.WriteBytes(buffer, ref offset, responseToken);
}
}
}

View file

@ -41,6 +41,11 @@
<Compile Include="Authentication\AuthenticateMessage\NegotiateMessage.cs" /> <Compile Include="Authentication\AuthenticateMessage\NegotiateMessage.cs" />
<Compile Include="Authentication\AuthenticateMessage\NTLMv2ClientChallenge.cs" /> <Compile Include="Authentication\AuthenticateMessage\NTLMv2ClientChallenge.cs" />
<Compile Include="Authentication\AuthenticateMessage\Version.cs" /> <Compile Include="Authentication\AuthenticateMessage\Version.cs" />
<Compile Include="Authentication\GSSAPI\DerEncodingHelper.cs" />
<Compile Include="Authentication\GSSAPI\GSSAPIHelper.cs" />
<Compile Include="Authentication\GSSAPI\SimpleProtectedNegotiationToken.cs" />
<Compile Include="Authentication\GSSAPI\SimpleProtectedNegotiationTokenInit.cs" />
<Compile Include="Authentication\GSSAPI\SimpleProtectedNegotiationTokenResponse.cs" />
<Compile Include="Authentication\MD4.cs" /> <Compile Include="Authentication\MD4.cs" />
<Compile Include="Authentication\NTAuthentication.cs" /> <Compile Include="Authentication\NTAuthentication.cs" />
<Compile Include="Client\SMBClient.cs" /> <Compile Include="Client\SMBClient.cs" />

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,
@ -119,18 +119,32 @@ namespace SMBLibrary.Server
{ {
SessionSetupAndXResponseExtended response = new SessionSetupAndXResponseExtended(); SessionSetupAndXResponseExtended response = new SessionSetupAndXResponseExtended();
bool isValid = AuthenticationMessageUtils.IsSignatureValid(request.SecurityBlob); // [MS-SMB] The Windows GSS implementation supports raw Kerberos / NTLM messages in the SecurityBlob
if (!isValid) byte[] messageBytes = request.SecurityBlob;
bool isRawMessage = true;
if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes))
{
messageBytes = GSSAPIHelper.GetNTLMSSPMessage(request.SecurityBlob);
isRawMessage = false;
}
if (!AuthenticationMessageUtils.IsSignatureValid(messageBytes))
{ {
header.Status = NTStatus.STATUS_NOT_IMPLEMENTED; header.Status = NTStatus.STATUS_NOT_IMPLEMENTED;
return new ErrorResponse(CommandName.SMB_COM_SESSION_SETUP_ANDX); return new ErrorResponse(CommandName.SMB_COM_SESSION_SETUP_ANDX);
} }
MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(request.SecurityBlob); MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(messageBytes);
if (messageType == MessageTypeName.Negotiate) if (messageType == MessageTypeName.Negotiate)
{ {
byte[] challengeMessageBytes = users.GetChallengeMessageBytes(request.SecurityBlob); byte[] challengeMessageBytes = users.GetChallengeMessageBytes(messageBytes);
if (isRawMessage)
{
response.SecurityBlob = challengeMessageBytes; response.SecurityBlob = challengeMessageBytes;
}
else
{
response.SecurityBlob = GSSAPIHelper.GetGSSTokenResponseBytesFromNTLMSSPMessage(challengeMessageBytes);
}
header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED; header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED;
} }
else // MessageTypeName.Authenticate else // MessageTypeName.Authenticate
@ -138,7 +152,7 @@ namespace SMBLibrary.Server
User user; User user;
try try
{ {
user = users.Authenticate(request.SecurityBlob); user = users.Authenticate(messageBytes);
} }
catch(EmptyPasswordNotAllowedException) catch(EmptyPasswordNotAllowedException)
{ {