Skip to content

Commit

Permalink
Always include the VERSION block in NTLM messages.
Browse files Browse the repository at this point in the history
Based on issue #1340 (and my re-reading of the spec)
  • Loading branch information
jstedfast committed Feb 25, 2022
1 parent cfa66e4 commit cfe6dba
Show file tree
Hide file tree
Showing 7 changed files with 44 additions and 48 deletions.
10 changes: 3 additions & 7 deletions MailKit/Security/Ntlm/NtlmAuthenticateMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -332,11 +332,7 @@ public override byte[] Encode ()
var target = EncodeString (Domain);
var user = EncodeString (UserName);
var workstation = EncodeString (Workstation);
int payloadOffset = 64, micOffset = -1;
bool negotiateVersion;

if (negotiateVersion = ((challenge.Flags & NtlmFlags.NegotiateVersion) != 0 && OSVersion != null))
payloadOffset += 8;
int payloadOffset = 72, micOffset = -1;

if (Mic != null) {
micOffset = payloadOffset;
Expand Down Expand Up @@ -424,11 +420,11 @@ public override byte[] Encode ()
message[62] = (byte)((uint) Flags >> 16);
message[63] = (byte)((uint) Flags >> 24);

if (negotiateVersion) {
if ((challenge.Flags & NtlmFlags.NegotiateVersion) != 0 && OSVersion != null) {
message[64] = (byte) OSVersion.Major;
message[65] = (byte) OSVersion.Minor;
message[66] = (byte) OSVersion.Build;
message[67] = (byte) (OSVersion.Build >> 8);
message[67] = (byte)(OSVersion.Build >> 8);
message[68] = 0x00;
message[69] = 0x00;
message[70] = 0x00;
Expand Down
17 changes: 5 additions & 12 deletions MailKit/Security/Ntlm/NtlmChallengeMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,11 +131,10 @@ public override byte[] Encode ()
return cached;

var targetInfo = GetEncodedTargetInfo ();
int targetNameOffset = 40;
int targetInfoOffset = 48;
int targetNameOffset = 48;
int targetInfoOffset = 56;
byte[] targetName = null;
bool negotiateVersion;
int size = 40;
int size = 48;

if (TargetName != null) {
var encoding = (Flags & NtlmFlags.NegotiateUnicode) != 0 ? Encoding.Unicode : Encoding.UTF8;
Expand All @@ -150,12 +149,6 @@ public override byte[] Encode ()
targetNameOffset += 8;
}

if (negotiateVersion = (Flags & NtlmFlags.NegotiateVersion) != 0) {
targetNameOffset += 8;
targetInfoOffset += 8;
size += 8;
}

// 12 bytes
var message = PrepareMessage (size);

Expand Down Expand Up @@ -198,11 +191,11 @@ public override byte[] Encode ()
Buffer.BlockCopy (targetInfo, 0, message, targetInfoOffset, targetInfo.Length);
}

if (negotiateVersion) {
if ((Flags & NtlmFlags.NegotiateVersion) != 0) {
message[48] = (byte) OSVersion.Major;
message[49] = (byte) OSVersion.Minor;
message[50] = (byte) OSVersion.Build;
message[51] = (byte) (OSVersion.Build >> 8);
message[51] = (byte)(OSVersion.Build >> 8);
message[52] = 0x00;
message[53] = 0x00;
message[54] = 0x00;
Expand Down
5 changes: 2 additions & 3 deletions MailKit/Security/Ntlm/NtlmNegotiateMessage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,9 @@ public override byte[] Encode ()
if (cached != null)
return cached;

var negotiateVersion = (Flags & NtlmFlags.NegotiateVersion) != 0;
var workstation = Encoding.UTF8.GetBytes (Workstation);
var domain = Encoding.UTF8.GetBytes (Domain);
int versionLength = negotiateVersion ? 8 : 0;
const int versionLength = 8;
int workstationOffset = 32 + versionLength;
int domainOffset = workstationOffset + workstation.Length;

Expand All @@ -145,7 +144,7 @@ public override byte[] Encode ()
message[28] = (byte) workstationOffset;
message[29] = (byte)(workstationOffset >> 8);

if (negotiateVersion) {
if ((Flags & NtlmFlags.NegotiateVersion) != 0) {
message[32] = (byte) OSVersion.Major;
message[33] = (byte) OSVersion.Minor;
message[34] = (byte) OSVersion.Build;
Expand Down
16 changes: 12 additions & 4 deletions MailKit/Security/SaslMechanismNtlm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ namespace MailKit.Security {
/// </remarks>
public class SaslMechanismNtlm : SaslMechanism
{
static readonly Version DefaultOSVersion;

enum LoginState {
Negotiate,
Challenge
Expand All @@ -51,6 +53,14 @@ enum LoginState {
bool negotiatedChannelBinding;
LoginState state;

static SaslMechanismNtlm ()
{
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
DefaultOSVersion = Environment.OSVersion.Version;
else
DefaultOSVersion = new Version (10, 0, 22000, 0);
}

#if NET48_OR_GREATER || NET5_0_OR_GREATER || NETSTANDARD2_0_OR_GREATER
/// <summary>
/// Initializes a new instance of the <see cref="MailKit.Security.SaslMechanismNtlm"/> class.
Expand All @@ -75,8 +85,7 @@ public SaslMechanismNtlm () : this (CredentialCache.DefaultNetworkCredentials)
/// </exception>
public SaslMechanismNtlm (NetworkCredential credentials) : base (credentials)
{
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
OSVersion = Environment.OSVersion.Version;
OSVersion = DefaultOSVersion;
Workstation = Environment.MachineName;
}

Expand All @@ -95,8 +104,7 @@ public SaslMechanismNtlm (NetworkCredential credentials) : base (credentials)
/// </exception>
public SaslMechanismNtlm (string userName, string password) : base (userName, password)
{
if (Environment.OSVersion.Platform == PlatformID.Win32NT)
OSVersion = Environment.OSVersion.Version;
OSVersion = DefaultOSVersion;
Workstation = Environment.MachineName;
}

Expand Down
4 changes: 2 additions & 2 deletions UnitTests/Security/Ntlm/NtlmChallengeMessageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public void TestEncodeJavaExample ()

Assert.AreEqual (2, type2.Type, "Type");
Assert.AreEqual ((NtlmFlags) 0x8201, type2.Flags, "Flags");
Assert.AreEqual ("4E-54-4C-4D-53-53-50-00-02-00-00-00-00-00-00-00-00-00-00-00-01-82-00-00-53-72-76-4E-6F-6E-63-65-00-00-00-00-00-00-00-00", BitConverter.ToString (type2.Encode ()), "Encode");
Assert.AreEqual ("TlRMTVNTUAACAAAAAAAAAAAAAAABggAAU3J2Tm9uY2UAAAAAAAAAAAAAAAAAAAAA", Convert.ToBase64String (type2.Encode ()), "Encode");
}

[Test]
Expand Down Expand Up @@ -98,7 +98,7 @@ public void TestEncodeDavenportExample ()
Assert.AreEqual ("server.domain.com", type2.TargetInfo.DnsServerName, "DnsServerName");
Assert.AreEqual ("domain.com", type2.TargetInfo.DnsDomainName, "DnsDomainName");
Assert.AreEqual ("01-23-45-67-89-AB-CD-EF", BitConverter.ToString (type2.ServerChallenge), "ServerChallenge");
Assert.AreEqual ("4E-54-4C-4D-53-53-50-00-02-00-00-00-0C-00-0C-00-30-00-00-00-01-02-81-00-01-23-45-67-89-AB-CD-EF-00-00-00-00-00-00-00-00-62-00-62-00-3C-00-00-00-44-00-4F-00-4D-00-41-00-49-00-4E-00-02-00-0C-00-44-00-4F-00-4D-00-41-00-49-00-4E-00-01-00-0C-00-53-00-45-00-52-00-56-00-45-00-52-00-04-00-14-00-64-00-6F-00-6D-00-61-00-69-00-6E-00-2E-00-63-00-6F-00-6D-00-03-00-22-00-73-00-65-00-72-00-76-00-65-00-72-00-2E-00-64-00-6F-00-6D-00-61-00-69-00-6E-00-2E-00-63-00-6F-00-6D-00-00-00-00-00", BitConverter.ToString (type2.Encode ()), "Encode");
Assert.AreEqual ("TlRMTVNTUAACAAAADAAMADgAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgBEAAAAAAAAAAAAAABEAE8ATQBBAEkATgACAAwARABPAE0AQQBJAE4AAQAMAFMARQBSAFYARQBSAAQAFABkAG8AbQBhAGkAbgAuAGMAbwBtAAMAIgBzAGUAcgB2AGUAcgAuAGQAbwBtAGEAaQBuAC4AYwBvAG0AAAAAAA==", Convert.ToBase64String (type2.Encode ()), "Encode");
}

[Test]
Expand Down
2 changes: 1 addition & 1 deletion UnitTests/Security/Ntlm/NtlmNegotiateMessageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ public void TestEncodeJavaExample ()

Assert.AreEqual (1, type1.Type, "Type");
Assert.AreEqual ((NtlmFlags) 0xb207, type1.Flags, "Flags");
Assert.AreEqual ("4E-54-4C-4D-53-53-50-00-01-00-00-00-07-B2-00-00-0A-00-0A-00-29-00-00-00-09-00-09-00-20-00-00-00-4C-49-47-48-54-43-49-54-59-55-52-53-41-2D-4D-49-4E-4F-52", BitConverter.ToString (type1.Encode ()), "Encode");
Assert.AreEqual ("TlRMTVNTUAABAAAAB7IAAAoACgAxAAAACQAJACgAAAAAAAAAAAAAAExJR0hUQ0lUWVVSU0EtTUlOT1I=", Convert.ToBase64String (type1.Encode ()), "Encode");
}

[Test]
Expand Down
38 changes: 19 additions & 19 deletions UnitTests/Security/SaslMechanismNtlmTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,10 @@ public void TestNtlmTargetInfoEncode ()

static readonly byte [] NtlmType1EncodedMessage = {
0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x01, 0x00, 0x00, 0x00,
0x07, 0xb2, 0x08, 0x20, 0x06, 0x00, 0x06, 0x00, 0x2b, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x0b, 0x00, 0x20, 0x00, 0x00, 0x00, 0x57, 0x4f, 0x52, 0x4b,
0x53, 0x54, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x44, 0x4f, 0x4d, 0x41, 0x49,
0x4e
0x07, 0xb2, 0x08, 0x20, 0x06, 0x00, 0x06, 0x00, 0x33, 0x00, 0x00, 0x00,
0x0b, 0x00, 0x0b, 0x00, 0x28, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x54, 0x41, 0x54,
0x49, 0x4f, 0x4e, 0x44, 0x4f, 0x4d, 0x41, 0x49, 0x4e
};

static readonly byte[] NtlmType1EncodedMessageWithVersion = {
Expand Down Expand Up @@ -236,19 +236,19 @@ public void TestNtlmNegotiateMessageDecodeWithVersion ()

static readonly byte[] NtlmType2EncodedMessage = {
0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00, 0x02, 0x00, 0x00, 0x00,
0x0c, 0x00, 0x0c, 0x00, 0x30, 0x00, 0x00, 0x00, 0x01, 0x02, 0x81, 0x00,
0x0c, 0x00, 0x0c, 0x00, 0x38, 0x00, 0x00, 0x00, 0x01, 0x02, 0x81, 0x00,
0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x62, 0x00, 0x3c, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x62, 0x00, 0x62, 0x00, 0x44, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x44, 0x00, 0x4f, 0x00,
0x4d, 0x00, 0x41, 0x00, 0x49, 0x00, 0x4e, 0x00, 0x02, 0x00, 0x0c, 0x00,
0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x41, 0x00, 0x49, 0x00, 0x4e, 0x00,
0x02, 0x00, 0x0c, 0x00, 0x44, 0x00, 0x4f, 0x00, 0x4d, 0x00, 0x41, 0x00,
0x49, 0x00, 0x4e, 0x00, 0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x45, 0x00,
0x52, 0x00, 0x56, 0x00, 0x45, 0x00, 0x52, 0x00, 0x04, 0x00, 0x14, 0x00,
0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00,
0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x03, 0x00, 0x22, 0x00,
0x73, 0x00, 0x65, 0x00, 0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00,
0x2e, 0x00, 0x64, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00,
0x6e, 0x00, 0x2e, 0x00, 0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00,
0x00, 0x00
0x01, 0x00, 0x0c, 0x00, 0x53, 0x00, 0x45, 0x00, 0x52, 0x00, 0x56, 0x00,
0x45, 0x00, 0x52, 0x00, 0x04, 0x00, 0x14, 0x00, 0x64, 0x00, 0x6f, 0x00,
0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00, 0x63, 0x00,
0x6f, 0x00, 0x6d, 0x00, 0x03, 0x00, 0x22, 0x00, 0x73, 0x00, 0x65, 0x00,
0x72, 0x00, 0x76, 0x00, 0x65, 0x00, 0x72, 0x00, 0x2e, 0x00, 0x64, 0x00,
0x6f, 0x00, 0x6d, 0x00, 0x61, 0x00, 0x69, 0x00, 0x6e, 0x00, 0x2e, 0x00,
0x63, 0x00, 0x6f, 0x00, 0x6d, 0x00, 0x00, 0x00, 0x00, 0x00
};

[Test]
Expand Down Expand Up @@ -379,7 +379,7 @@ public void TestNtlmChallengeMessageDecodeWithOSVersion ()
[Test]
public void TestNtlmAuthenticateMessageEncode ()
{
const string expected = "TlRMTVNTUAADAAAAGAAYAGoAAACqAKoAggAAAAwADABAAAAACAAIAEwAAAAWABYAVAAAAAAAAAAsAQAAAQKBAEQATwBNAEEASQBOAHUAcwBlAHIAVwBPAFIASwBTAFQAQQBUAEkATwBOAAFVaqKdtK6RUUd6vhq2MnkBAgMEBQUGByItsEaz8xYhhLclKBEweI0BAQAAAAAAAACQVAPpkdcBAQIDBAUFBgcAAAAAAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAkAAAAKABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
const string expected = "TlRMTVNTUAADAAAAGAAYAHIAAACqAKoAigAAAAwADABIAAAACAAIAFQAAAAWABYAXAAAAAAAAAA0AQAAAQKBAAAAAAAAAAAARABPAE0AQQBJAE4AdQBzAGUAcgBXAE8AUgBLAFMAVABBAFQASQBPAE4AAVVqop20rpFRR3q+GrYyeQECAwQFBQYHIi2wRrPzFiGEtyUoETB4jQEBAAAAAAAAAJBUA+mR1wEBAgMEBQUGBwAAAAACAAwARABPAE0AQQBJAE4AAQAMAFMARQBSAFYARQBSAAQAFABkAG8AbQBhAGkAbgAuAGMAbwBtAAMAIgBzAGUAcgB2AGUAcgAuAGQAbwBtAGEAaQBuAC4AYwBvAG0ACQAAAAoAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=";
const string challenge2 = "TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA=";
var flags = NtlmFlags.NegotiateUnicode | NtlmFlags.NegotiateNtlm | NtlmFlags.TargetTypeDomain | NtlmFlags.NegotiateTargetInfo;
var timestamp = new DateTime (2021, 08, 15, 15, 20, 00, DateTimeKind.Utc).Ticks;
Expand Down Expand Up @@ -533,7 +533,7 @@ static void AssertNtlmv2 (SaslMechanismNtlm sasl, string challenge1, string chal
[Test]
public void TestAuthenticationNtlmv2 ()
{
const string challenge1 = "TlRMTVNTUAABAAAAB4IIoAAAAAAgAAAAAAAAACAAAAA=";
const string challenge1 = "TlRMTVNTUAABAAAAB4IIoAAAAAAoAAAAAAAAACgAAAAAAAAAAAAAAA==";
const string challenge2 = "TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA=";
var credentials = new NetworkCredential ("user", "password");
var sasl = new SaslMechanismNtlm (credentials) { OSVersion = null, Workstation = null };
Expand All @@ -544,7 +544,7 @@ public void TestAuthenticationNtlmv2 ()
[Test]
public void TestAuthenticationNtlmv2WithDomain ()
{
const string challenge1 = "TlRMTVNTUAABAAAAB5IIoAYABgAgAAAAAAAAACAAAABET01BSU4=";
const string challenge1 = "TlRMTVNTUAABAAAAB5IIoAYABgAoAAAAAAAAACgAAAAAAAAAAAAAAERPTUFJTg==";
const string challenge2 = "TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA=";
var credentials = new NetworkCredential ("user", "password", "DOMAIN");
var sasl = new SaslMechanismNtlm (credentials) { OSVersion = null, Workstation = null };
Expand All @@ -555,7 +555,7 @@ public void TestAuthenticationNtlmv2WithDomain ()
[Test]
public void TestAuthenticationNtlmv2WithDomainAndWorkstation ()
{
const string challenge1 = "TlRMTVNTUAABAAAAB7IIoAYABgArAAAACwALACAAAABXT1JLU1RBVElPTkRPTUFJTg==";
const string challenge1 = "TlRMTVNTUAABAAAAB7IIoAYABgAzAAAACwALACgAAAAAAAAAAAAAAFdPUktTVEFUSU9ORE9NQUlO";
const string challenge2 = "TlRMTVNTUAACAAAADAAMADAAAAABAoEAASNFZ4mrze8AAAAAAAAAAGIAYgA8AAAARABPAE0AQQBJAE4AAgAMAEQATwBNAEEASQBOAAEADABTAEUAUgBWAEUAUgAEABQAZABvAG0AYQBpAG4ALgBjAG8AbQADACIAcwBlAHIAdgBlAHIALgBkAG8AbQBhAGkAbgAuAGMAbwBtAAAAAAA=";
var credentials = new NetworkCredential ("user", "password", "DOMAIN");
var sasl = new SaslMechanismNtlm (credentials) { OSVersion = null, Workstation = "WORKSTATION" };
Expand Down

0 comments on commit cfe6dba

Please sign in to comment.