2008-03-05 09:52:00 -05:00
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
|
|
// Microsoft Research Singularity
|
|
|
|
//
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
//
|
|
|
|
// File: NtlmUtil.sg
|
|
|
|
//
|
|
|
|
// Note:
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
using System;
|
|
|
|
using System.Diagnostics;
|
|
|
|
using System.Text;
|
|
|
|
using Microsoft.Singularity;
|
|
|
|
using Util = Utils.Util;
|
|
|
|
|
|
|
|
namespace System.Security.Protocols.Ntlm
|
|
|
|
{
|
|
|
|
public class NtlmUtil
|
|
|
|
{
|
|
|
|
public static ushort GetUInt16(byte[]! message, int pos)
|
|
|
|
{
|
|
|
|
return (ushort)(message[pos] + (message[pos + 1] << 8));
|
|
|
|
}
|
|
|
|
|
|
|
|
public static byte[]! GetCountedBytesAt(byte[]! message, int pos)
|
|
|
|
requires pos >= 0;
|
|
|
|
requires pos < message.Length;
|
|
|
|
{
|
|
|
|
int length = GetUInt16(message, pos + 0);
|
|
|
|
//int maxlength = GetUInt16(message, pos + 2);
|
|
|
|
int offset = GetUInt16(message, pos + 4);
|
|
|
|
|
|
|
|
if (offset >= message.Length)
|
|
|
|
throw new Exception("String has invalid offset");
|
|
|
|
|
|
|
|
if (offset + length > message.Length)
|
|
|
|
throw new Exception("String has invalid offset / length");
|
|
|
|
|
|
|
|
return NtlmUtil.GetSubArray(message, offset, length);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static byte[]! GetSubArray(byte[]! arr, int index, int length)
|
|
|
|
requires index >= 0;
|
|
|
|
requires index + length <= arr.Length;
|
|
|
|
ensures result.Length == length;
|
|
|
|
{
|
|
|
|
byte[] result = new byte[length];
|
|
|
|
Array.Copy(arr, index, result, 0, length);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
public static string GetCountedStringAt(byte[]! message, int pos)
|
|
|
|
requires pos >= 0;
|
|
|
|
requires pos + sizeof(BufferRegion) <= message.Length;
|
|
|
|
{
|
|
|
|
ref BufferRegion region = ref message[pos];
|
|
|
|
|
|
|
|
int length = ByteOrder.UInt16LeToHost(region.Length);
|
|
|
|
// int maxlength = GetUInt16(message, pos + 2);
|
|
|
|
int offset = ByteOrder.UInt16LeToHost(region.Offset);
|
|
|
|
|
|
|
|
if (offset >= message.Length)
|
|
|
|
throw new Exception("String has invalid offset");
|
|
|
|
|
|
|
|
if (offset + length > message.Length)
|
|
|
|
throw new Exception("String has invalid offset / length");
|
|
|
|
|
|
|
|
string result = Encoding.Unicode.GetString(message, offset, length);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if NOISY
|
|
|
|
|
|
|
|
public static void DumpMessage(byte[]! message)
|
|
|
|
{
|
|
|
|
DumpMessage(message, message.Length);
|
|
|
|
}
|
|
|
|
|
|
|
|
public static void DumpMessage(byte[]! message, int length)
|
|
|
|
{
|
|
|
|
DebugLine("");
|
|
|
|
DebugLine("NTLM message:");
|
|
|
|
Util.DumpBuffer(message, 0, length);
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
if (length < NtlmConstants.HeaderLength) {
|
2008-03-05 09:52:00 -05:00
|
|
|
DebugLine(" Message is invalid; too short");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
for (int i = 0; i < NtlmConstants.MessageSignature.Length; i++) {
|
|
|
|
if (message[i] != NtlmConstants.MessageSignature[i]) {
|
2008-03-05 09:52:00 -05:00
|
|
|
DebugLine(" Message is invalid; signature does not match");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NtlmMessageType type = (NtlmMessageType)message[8];
|
|
|
|
|
2008-11-17 18:29:00 -05:00
|
|
|
switch (type) {
|
2008-03-05 09:52:00 -05:00
|
|
|
case NtlmMessageType.Negotiate:
|
|
|
|
{
|
|
|
|
DebugLine(" Type: Negotiate");
|
|
|
|
NtlmNegotiateFlags flags = (NtlmNegotiateFlags)GetUInt16(message, 12);
|
|
|
|
DebugLine(String.Format(" Flags: 0x{0:x8} {1}", (UInt32)flags, flags.ToString()));
|
|
|
|
string domain = GetCountedStringAt(message, 0x10);
|
|
|
|
DebugLine(" Domain: " + domain);
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NtlmMessageType.Challenge:
|
|
|
|
{
|
|
|
|
DebugLine(" Type: Challenge");
|
|
|
|
string TargetName = GetCountedStringAt(message, 0x0c);
|
|
|
|
byte[] Challenge = Util.GetSubArray(message, 0x14, NtlmConstants.ChallengeLength);
|
|
|
|
DebugLine(" TargetName: " + TargetName);
|
|
|
|
DebugLine(" Challenge: " + Util.ByteArrayToStringHex(Challenge));
|
|
|
|
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NtlmMessageType.Response:
|
|
|
|
{
|
|
|
|
DebugLine(" Type: Response");
|
|
|
|
byte[] LmChallengeResponse = GetCountedbytesAt(message, 0x0c);
|
|
|
|
byte[] NtChallengeResponse = GetCountedbytesAt(message, 0x14);
|
|
|
|
string DomainName = GetCountedStringAt(message, 28);
|
|
|
|
string UserName = GetCountedStringAt(message, 36);
|
|
|
|
string Workstation = GetCountedStringAt(message, 42);
|
|
|
|
|
|
|
|
DebugLine(" LmChallengeResponse: " + Util.ByteArrayToStringHex(LmChallengeResponse));
|
|
|
|
DebugLine(" NtChallengeResponse: " + Util.ByteArrayToStringHex(NtChallengeResponse));
|
|
|
|
|
|
|
|
DebugLine(" DomainName: " + DomainName);
|
|
|
|
DebugLine(" UserName: " + UserName);
|
|
|
|
DebugLine(" Workstation: " + Workstation);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
DebugLine(" Message is invalid; message type byte is not recognized");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
DebugLine("");
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
static void DebugLine(string msg)
|
|
|
|
{
|
|
|
|
DebugStub.WriteLine("NTLM: " + msg);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|