[Mono-dev] patch for (and question about) Mono.Security.Protocol.Ntlm

Sebastien Pouliot sebastien at ximian.com
Tue Mar 27 10:25:47 EDT 2007


On Tue, 2007-03-27 at 04:50 +0900, Atsushi Eno wrote:
> Hi,
> 
> During WCF hacking I found that Mono.Security.Protocol.Ntlm looks
> based on somewhat old analysis.

IIRC (I'm not feeling well enough to fully trust my memory today) I did
this code when Daniel Morgan asked (2003) my help on the SQL Server
authentication issues he had (integrated security). Later on (2004), it
was "adapted" to be used for HttpWebRequest.

But being old isn't a problem in itself as NTLM, a successor to OS/2 1.x
LM, is quite old.

> Currently the code does not look "version" aware. According to
> http://davenport.sourceforge.net/ntlm.html , 

The two unofficial specs I had at the time, including an earlier version
of your link, did not agree 100% with each other. So I merged a few
things (from both).

> there seems three
> ntlm versions and the message layout is diffrent for each version.
> And WCF SSPI negotiation seems based on version3, which is not
> what current code supports.

That would make sense. NTLM always tried to be backward compatible with
earlier version and that caused a lot of problems over the years.

> So I have created a patch to add support for every version of NTLM.
> It is not a small, but the purpose is to handle "optional" fields
> denoted in the document above.

Great work! I'll need to check a few things, like API forward
compatibility before it's committed.

> This patch however breaks some NUnit tests. 

Which ones ?

> But as long as I see
> the test buffers, they do not seem conformant to the document
> above... how were those test buffers created?

Most of them have an URL from where the data was taken to build the test
case. They were part of the (earlier) specs so if the unofficial specs
were updated maybe the samples were too. 

However it also means that the code was working "as is", so it may hide
other issues too.

A good way to be sure there's no regression is to test the code against
SQL Server 2000+ and against IIS with integrated security.

> (Also, existing message type1 looks based on version 2, while
> message type2 and message type3 do not look so, but I'm not sure.
> They hence resulted in weird default version mismatch in my patch...)
> 
> Atsushi Eno
> plain text document attachment (version-aware-ntlm.patch)
> Index: Mono.Security.Protocol.Ntlm/MessageBase.cs
> ===================================================================
> --- Mono.Security.Protocol.Ntlm/MessageBase.cs	(revision 74928)
> +++ Mono.Security.Protocol.Ntlm/MessageBase.cs	(working copy)
> @@ -4,9 +4,10 @@
>  //
>  // Author:
>  //	Sebastien Pouliot  <sebastien at ximian.com>
> +//	Atsushi Enomoto <atsushi at ximian.com>
>  //
>  // Copyright (C) 2003 Motus Technologies Inc. (http://www.motus.com)
> -// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
> +// Copyright (C) 2004, 2007 Novell, Inc (http://www.novell.com)
>  //
>  // References
>  // a.	NTLM Authentication Scheme for HTTP, Ronald Tschalär
> @@ -40,15 +41,34 @@
>  namespace Mono.Security.Protocol.Ntlm {
>  
>  	public abstract class MessageBase {
> +		static byte [] _current_os_version = GetOSVersion ();
>  
> +		static byte [] GetOSVersion ()
> +		{
> +			Version v = Environment.OSVersion.Version;
> +			byte [] bytes = new byte [8];
> +			bytes [0] = (byte) v.Major;
> +			bytes [1] = (byte) v.Minor;
> +			bytes [2] = (byte) v.Build;
> +			bytes [3] = (byte) (v.Build >> 8);
> +			bytes [7] = 0xF;
> +			return bytes;
> +		}
> +
>  		static private byte[] header = { 0x4e, 0x54, 0x4c, 0x4d, 0x53, 0x53, 0x50, 0x00 };
>  		
>  		private int _type;
>  		private NtlmFlags _flags;
> +		private NtlmVersion _version;
> +		private byte [] _osversion = _current_os_version;
>  
> -		protected MessageBase (int messageType) 
> +		protected MessageBase (int messageType) : this (messageType, NtlmVersion.Version1)
>  		{
> +		}
> +		protected MessageBase (int messageType, NtlmVersion version) 
> +		{
>  			_type = messageType;
> +			_version = version;
>  		}
>  		
>  		public NtlmFlags Flags {
> @@ -56,10 +76,19 @@
>  			set { _flags = value; }
>  		}
>  
> +		public byte [] OSVersion {
> +			get { return (byte []) _osversion.Clone (); }
> +			set { _osversion = (byte []) value.Clone (); }
> +		}
> +
>  		public int Type { 
>  			get { return _type; }
>  		}
>  
> +		public NtlmVersion Version {
> +			get { return _version; }
> +		}
> +
>  		protected byte[] PrepareMessage (int messageSize) 
>  		{
>  			byte[] message = new byte [messageSize];
> @@ -100,5 +129,12 @@
>  		}
>  
>  		public abstract byte[] GetBytes ();
> +
> +		internal byte [] CreateSubArray (byte [] source, int offset, int length)
> +		{
> +			byte [] ret = new byte [length];
> +			Array.Copy (source, offset, ret, 0, length);
> +			return ret;
> +		}
>  	}
>  }
> Index: Mono.Security.Protocol.Ntlm/NtlmVersion.cs
> ===================================================================
> --- Mono.Security.Protocol.Ntlm/NtlmVersion.cs	(revision 0)
> +++ Mono.Security.Protocol.Ntlm/NtlmVersion.cs	(revision 0)
> @@ -0,0 +1,41 @@
> +//
> +// Mono.Security.Protocol.Ntlm.NtlmVersion
> +//
> +// Author:
> +//	Atsushi Enomoto <atsushi at ximian.com>
> +//
> +// (C) 2007 Novell, Inc. (http://www.novell.com)
> +//
> +
> +//
> +// Permission is hereby granted, free of charge, to any person obtaining
> +// a copy of this software and associated documentation files (the
> +// "Software"), to deal in the Software without restriction, including
> +// without limitation the rights to use, copy, modify, merge, publish,
> +// distribute, sublicense, and/or sell copies of the Software, and to
> +// permit persons to whom the Software is furnished to do so, subject to
> +// the following conditions:
> +// 
> +// The above copyright notice and this permission notice shall be
> +// included in all copies or substantial portions of the Software.
> +// 
> +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
> +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
> +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
> +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> +//
> +
> +using System;
> +
> +namespace Mono.Security.Protocol.Ntlm 
> +{
> +	public enum NtlmVersion
> +	{
> +		Version1,
> +		Version2,
> +		Version3
> +	}
> +}
> Index: Mono.Security.Protocol.Ntlm/ChallengeResponse.cs
> ===================================================================
> --- Mono.Security.Protocol.Ntlm/ChallengeResponse.cs	(revision 74928)
> +++ Mono.Security.Protocol.Ntlm/ChallengeResponse.cs	(working copy)
> @@ -4,9 +4,10 @@
>  //
>  // Author:
>  //	Sebastien Pouliot <sebastien at ximian.com>
> +//	Atsushi Enomoto <atsushi at ximian.com>
>  //
>  // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
> -// (C) 2004 Novell (http://www.novell.com)
> +// (C) 2004, 2007 Novell (http://www.novell.com)
>  //
>  // References
>  // a.	NTLM Authentication Scheme for HTTP, Ronald Tschalär
> @@ -156,6 +157,29 @@
>  			}
>  		}
>  
> +		public byte [] LMSessionKey {
> +			get {
> +				if (_disposed)
> +					throw new ObjectDisposedException ("too late");
> +
> +				byte[] lm = LM;
> +				byte[] pwd = new byte [14];
> +				Buffer.BlockCopy (lm, 0, pwd, 0, 8);
> +				for (int i = 8; i < 14; i++)
> +					pwd [i] = 0xBD;
> +				byte[] response = new byte [16];
> +				DES des = DES.Create ();
> +				des.Mode = CipherMode.ECB;
> +				des.Key = PrepareDESKey (pwd, 0);
> +				ICryptoTransform ct = des.CreateEncryptor ();
> +				ct.TransformBlock (lm, 0, 8, response, 0);
> +				des.Key = PrepareDESKey (pwd, 7);
> +				ct = des.CreateEncryptor ();
> +				ct.TransformBlock (lm, 0, 8, response, 8);
> +				return response;
> +			}
> +		}
> +
>  		// IDisposable method
>  
>  		public void Dispose () 
> Index: Mono.Security.Protocol.Ntlm/NtlmFlags.cs
> ===================================================================
> --- Mono.Security.Protocol.Ntlm/NtlmFlags.cs	(revision 74928)
> +++ Mono.Security.Protocol.Ntlm/NtlmFlags.cs	(working copy)
> @@ -3,9 +3,10 @@
>  //
>  // Author:
>  //	Sebastien Pouliot <sebastien at ximian.com>
> +//	Atsushi Enomoto <atsushi at ximian.com>
>  //
>  // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
> -// (C) 2004 Novell (http://www.novell.com)
> +// (C) 2004, 2007 Novell (http://www.novell.com)
>  //
>  // References
>  // a.	NTLM Authentication Scheme for HTTP, Ronald Tschalär
> @@ -47,8 +48,18 @@
>  		NegotiateOem = 0x00000002,
>  		// This requests that the server send the authentication target with the Type 2 reply.
>  		RequestTarget = 0x00000004,
> +		// Negotiate Sign
> +		NegotiateSign = 0x00000010,
> +		// Negotiate Seal
> +		NegotiateSeal = 0x00000020,
> +		// Negotiate DatagramStyle
> +		NegotiateDatagramStyle = 0x00000040,
> +		// Negotiate Lan Manager Key
> +		NegotiateLm = 0x00000080,
>  		// Indicates that NTLM authentication is supported.
>  		NegotiateNtlm = 0x00000200,
> +		// Indicates that NTLM authentication is supported.
> +		NegotiateAnonymous = 0x00000800,
>  		// When set, the client will send with the message the name of the domain in which the workstation has membership.
>  		NegotiateDomainSupplied = 0x00001000,
>  		// Indicates that the client is sending its workstation name with the message.  
> @@ -59,6 +70,8 @@
>  		NegotiateNtlm2Key = 0x00080000,
>  		// Indicates that this client supports strong (128-bit) encryption.
>  		Negotiate128 = 0x20000000,
> +		// Negotiate Key Exchange
> +		NegotiateKeyExchange = 0x40000000,
>  		// Indicates that this client supports medium (56-bit) encryption.
>  		Negotiate56 = (unchecked ((int) 0x80000000))
>  	}
> Index: Mono.Security.Protocol.Ntlm/Type1Message.cs
> ===================================================================
> --- Mono.Security.Protocol.Ntlm/Type1Message.cs	(revision 74928)
> +++ Mono.Security.Protocol.Ntlm/Type1Message.cs	(working copy)
> @@ -3,9 +3,10 @@
>  //
>  // Author:
>  //	Sebastien Pouliot <sebastien at ximian.com>
> +//	Atsushi Enomoto <atsushi at ximian.com>
>  //
>  // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
> -// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
> +// Copyright (C) 2004, 2007 Novell, Inc (http://www.novell.com)
>  //
>  // References
>  // a.	NTLM Authentication Scheme for HTTP, Ronald Tschalär
> @@ -44,16 +45,24 @@
>  		private string _host;
>  		private string _domain;
>  
> -		public Type1Message () : base (1)
> +		public Type1Message () : this (NtlmVersion.Version2)
>  		{
> +		}
> +
> +		public Type1Message (NtlmVersion version) : base (1, version)
> +		{
>  			// default values
>  			_domain = Environment.UserDomainName;
>  			_host = Environment.MachineName;
>  			Flags = (NtlmFlags) 0xb203;
>  		}
>  
> -		public Type1Message (byte[] message) : base (1)
> +		public Type1Message (byte[] message) : this (message, NtlmVersion.Version2)
>  		{
> +		}
> +
> +		public Type1Message (byte[] message, NtlmVersion version) : base (1, version)
> +		{
>  			Decode (message);
>  		}
>  
> @@ -76,28 +85,44 @@
>  			base.Decode (message);
>  
>  			Flags = (NtlmFlags) BitConverterLE.ToUInt32 (message, 12);
> +			if (Version == NtlmVersion.Version1)
> +				return;
>  
>  			int dom_len = BitConverterLE.ToUInt16 (message, 16);
>  			int dom_off = BitConverterLE.ToUInt16 (message, 20);
>  			_domain = Encoding.ASCII.GetString (message, dom_off, dom_len);
>  
>  			int host_len = BitConverterLE.ToUInt16 (message, 24);
> -			_host = Encoding.ASCII.GetString (message, 32, host_len);
> +			int host_off = BitConverterLE.ToUInt16 (message, 28);
> +			_host = Encoding.ASCII.GetString (message, host_off, host_len);
> +
> +			if (Version != NtlmVersion.Version3)
> +				return;
> +			OSVersion = CreateSubArray (message, 32, 8);
>  		}
>  
>  		public override byte[] GetBytes () 
>  		{
> -			short dom_len = (short) _domain.Length;
> -			short host_len = (short) _host.Length;
> +			short dom_len = 0, host_len = 0;
> +			if (Version != NtlmVersion.Version1) {
> +				dom_len = (short) _domain.Length;
> +				host_len = (short) _host.Length;
> +			}
>  
> -			byte[] data = PrepareMessage (32 + dom_len + host_len);
> +			int headSize = (Version == NtlmVersion.Version3 ? 40 : 32);
>  
> +			byte[] data = PrepareMessage (headSize + dom_len + host_len);
> +
> +			// v1 contains only the flags.
> +			if (Version == NtlmVersion.Version1)
> +				return data;
> +
>  			data [12] = (byte) Flags;
>  			data [13] = (byte)((uint)Flags >> 8);
>  			data [14] = (byte)((uint)Flags >> 16);
>  			data [15] = (byte)((uint)Flags >> 24);
>  
> -			short dom_off = (short)(32 + host_len);
> +			short dom_off = (short) (headSize + host_len);
>  
>  			data [16] = (byte) dom_len;
>  			data [17] = (byte)(dom_len >> 8);
> @@ -113,8 +138,11 @@
>  			data [28] = 0x20;
>  			data [29] = 0x00;
>  
> +			if (Version == NtlmVersion.Version3)
> +				Buffer.BlockCopy (OSVersion, 0, data, 32, OSVersion.Length);
> +
>  			byte[] host = Encoding.ASCII.GetBytes (_host.ToUpper (CultureInfo.InvariantCulture));
> -			Buffer.BlockCopy (host, 0, data, 32, host.Length);
> +			Buffer.BlockCopy (host, 0, data, headSize, host.Length);
>  
>  			byte[] domain = Encoding.ASCII.GetBytes (_domain.ToUpper (CultureInfo.InvariantCulture));
>  			Buffer.BlockCopy (domain, 0, data, dom_off, domain.Length);
> Index: Mono.Security.Protocol.Ntlm/NtlmTargetInformation.cs
> ===================================================================
> --- Mono.Security.Protocol.Ntlm/NtlmTargetInformation.cs	(revision 0)
> +++ Mono.Security.Protocol.Ntlm/NtlmTargetInformation.cs	(revision 0)
> @@ -0,0 +1,103 @@
> +//
> +// Mono.Security.Protocol.Ntlm.NtlmTargetInformation
> +//
> +// Author:
> +//	Atsushi Enomoto  <atsushi at ximian.com>
> +//
> +// (C) 2007 Novell, Inc. (http://www.novell.com)
> +//
> +
> +//
> +// Permission is hereby granted, free of charge, to any person obtaining
> +// a copy of this software and associated documentation files (the
> +// "Software"), to deal in the Software without restriction, including
> +// without limitation the rights to use, copy, modify, merge, publish,
> +// distribute, sublicense, and/or sell copies of the Software, and to
> +// permit persons to whom the Software is furnished to do so, subject to
> +// the following conditions:
> +// 
> +// The above copyright notice and this permission notice shall be
> +// included in all copies or substantial portions of the Software.
> +// 
> +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
> +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
> +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
> +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
> +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
> +//
> +using System;
> +using System.IO;
> +using System.Text;
> +
> +namespace Mono.Security.Protocol.Ntlm {
> +	public class NtlmTargetInformation {
> +		string _server, _domain, _dns_host, _dns_domain;
> +
> +		public string ServerName {
> +			get { return _server; }
> +			set { _server = value; }
> +		}
> +
> +		public string DomainName {
> +			get { return _domain; }
> +			set { _domain = value; }
> +		}
> +
> +		public string DnsHostName {
> +			get { return _dns_host; }
> +			set { _dns_host = value; }
> +		}
> +
> +		public string DnsDomainName {
> +			get { return _dns_domain; }
> +			set { _dns_domain = value; }
> +		}
> +
> +		public void Decode (byte [] bytes, int length, int offset)
> +		{
> +			int end = offset + length;
> +			for (int pos = offset; pos < end;) {
> +				short type = BitConverterLE.ToInt16 (bytes, pos); // reader.ReadInt16 ();
> +				short blen = BitConverterLE.ToInt16 (bytes, pos + 2); // reader.ReadInt16 ();
> +				string s = Encoding.Unicode.GetString (bytes, pos + 4, blen);
> +				pos += blen + 4;
> +				switch (type) {
> +				case 0: break; // terminator
> +				case 1: ServerName = s; break;
> +				case 2: DomainName = s; break;
> +				case 3: DnsHostName = s; break;
> +				case 4: DnsDomainName = s; break;
> +				default:
> +					throw new ArgumentException (String.Format ("Invalid SSPI message type 2 subblock type: {0}", type));
> +				}
> +				if (type == 0)
> +					break; // terminator subblock
> +			}
> +		}
> +
> +		public byte [] ToBytes ()
> +		{
> +			MemoryStream ms = new MemoryStream ();
> +			BinaryWriter bw = new BinaryWriter (ms);
> +
> +			WriteName (bw, 1, ServerName);
> +			WriteName (bw, 2, DomainName);
> +			WriteName (bw, 3, DnsHostName);
> +			WriteName (bw, 4, DnsDomainName);
> +			bw.Close ();
> +			return ms.ToArray ();
> +		}
> +
> +		private void WriteName (BinaryWriter bw, short type, string value)
> +		{
> +			if (value == null)
> +				return;
> +			byte [] bytes = Encoding.Unicode.GetBytes (value);
> +			bw.Write (type);
> +			bw.Write ((short) bytes.Length);
> +			bw.Write (bytes);
> +		}
> +	}
> +}
> Index: Mono.Security.Protocol.Ntlm/Type2Message.cs
> ===================================================================
> --- Mono.Security.Protocol.Ntlm/Type2Message.cs	(revision 74928)
> +++ Mono.Security.Protocol.Ntlm/Type2Message.cs	(working copy)
> @@ -3,9 +3,10 @@
>  //
>  // Author:
>  //	Sebastien Pouliot <sebastien at ximian.com>
> +//	Atsushi Enomoto <atsushi at ximian.com>
>  //
>  // Copyright (C) 2003 Motus Technologies Inc. (http://www.motus.com)
> -// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
> +// Copyright (C) 2004, 2007 Novell, Inc (http://www.novell.com)
>  //
>  // References
>  // a.	NTLM Authentication Scheme for HTTP, Ronald Tschalär
> @@ -35,24 +36,40 @@
>  
>  using System;
>  using System.Security.Cryptography;
> +using System.Text;
>  
>  namespace Mono.Security.Protocol.Ntlm {
>  
>  	public class Type2Message : MessageBase {
>  
>  		private byte[] _nonce;
> +		private byte[] _context;
> +		private NtlmTargetInformation _target;
> +		private string _target_name;
>  
> -		public Type2Message () : base (2)
> +		public Type2Message () : this (NtlmVersion.Version1)
>  		{
> +		}
> +
> +		public Type2Message (NtlmVersion version) : base (2, version)
> +		{
>  			_nonce = new byte [8];
>  			RandomNumberGenerator rng = RandomNumberGenerator.Create ();
>  			rng.GetBytes (_nonce);
>  			// default values
>  			Flags = (NtlmFlags) 0x8201;
> +			if (Version != NtlmVersion.Version1) {
> +				_context = new byte [8];
> +				_target = new NtlmTargetInformation ();
> +			}
>  		}
>  
> -		public Type2Message (byte[] message) : base (2)
> +		public Type2Message (byte[] message) : this (message, NtlmVersion.Version1)
>  		{
> +		}
> +
> +		public Type2Message (byte[] message, NtlmVersion version) : base (2, version)
> +		{
>  			_nonce = new byte [8];
>  			Decode (message);
>  		}
> @@ -65,6 +82,19 @@
>  
>  		// properties
>  
> +		public byte[] Context {
> +			get { return (byte[]) _context.Clone (); }
> +			set { 
> +				if (value == null)
> +					throw new ArgumentNullException ("Nonce");
> +				if (value.Length != 8) {
> +					string msg = Locale.GetText ("Invalid Nonce Length (should be 8 bytes).");
> +					throw new ArgumentException (msg, "Nonce");
> +				}
> +				_context = (byte[]) value.Clone (); 
> +			}
> +		}
> +
>  		public byte[] Nonce {
>  			get { return (byte[]) _nonce.Clone (); }
>  			set { 
> @@ -78,26 +108,76 @@
>  			}
>  		}
>  
> +		public NtlmTargetInformation Target {
> +			get { return _target; }
> +		}
> +
> +		public string TargetName {
> +			get { return _target_name; }
> +			set { _target_name = value; }
> +		}
> +
>  		// methods
>  
>  		protected override void Decode (byte[] message) 
>  		{
>  			base.Decode (message);
>  
> +			short targetNameSize = BitConverterLE.ToInt16 (message, 12);
> +			int targetNameOffset = BitConverterLE.ToInt32 (message, 16);
> +
>  			Flags = (NtlmFlags) BitConverterLE.ToUInt32 (message, 20);
>  
>  			Buffer.BlockCopy (message, 24, _nonce, 0, 8);
> +
> +			if (Version == NtlmVersion.Version1)
> +				return;
> +
> +			Buffer.BlockCopy (message, 32, _context, 0, 8);
> +			short targetInfoSize = BitConverterLE.ToInt16 (message, 40);
> +			int targetInfoOffset = BitConverterLE.ToInt32 (message, 44);
> +
> +			if (Version == NtlmVersion.Version3)
> +				Buffer.BlockCopy (OSVersion, 0, message, 48, OSVersion.Length);
> +
> +			Encoding enc = (Flags & NtlmFlags.NegotiateUnicode) != 0 ? Encoding.Unicode : Encoding.UTF8;
> +			if (targetNameSize > 0)
> +				TargetName = enc.GetString (message, targetNameOffset, targetNameSize);
> +
> +			_target.Decode (message, targetInfoOffset, targetInfoSize);
>  		}
>  
>  		public override byte[] GetBytes ()
>  		{
> -			byte[] data = PrepareMessage (40);
> +			byte [] name_bytes = null, target = null;
> +			short name_len = 0, target_len = 0;
> +			if (TargetName != null) {
> +				Encoding enc = (Flags & NtlmFlags.NegotiateUnicode) != 0 ? Encoding.Unicode : Encoding.UTF8;
> +				name_bytes = enc.GetBytes (TargetName);
> +				name_len = (short) name_bytes.Length;
> +			}
> +			if (Version != NtlmVersion.Version1) {
> +				target = _target.ToBytes ();
> +				target_len = (short) target.Length;
> +			}
>  
> -			// message length
> -			short msg_len = (short)data.Length;
> -			data [16] = (byte) msg_len;
> -			data [17] = (byte)(msg_len >> 8);
> +			uint name_offset = (uint) (Version == NtlmVersion.Version3 ? 56 : 48);
>  
> +			int size = (int) name_offset +
> +				   (name_len > 0 ? name_len + 8 : 0) +
> +				   (target_len > 0 ? target_len + 8 : 0);
> +			byte[] data = PrepareMessage (size);
> +
> +			// target name
> +			data [12] = (byte) name_len;
> +			data [13] = (byte) (name_len >> 8);
> +			data [14] = data [12];
> +			data [15] = data [13];
> +			data [16] = (byte) name_offset;
> +			data [17] = (byte) (name_offset >> 8);
> +			data [18] = (byte) (name_offset >> 16);
> +			data [19] = (byte) (name_offset >> 24);
> +
>  			// flags
>  			data [20] = (byte) Flags;
>  			data [21] = (byte)((uint)Flags >> 8);
> @@ -105,6 +185,30 @@
>  			data [23] = (byte)((uint)Flags >> 24);
>  
>  			Buffer.BlockCopy (_nonce, 0, data, 24, _nonce.Length);
> +
> +			if (Version == NtlmVersion.Version1)
> +				return data;
> +
> +			// context
> +			Buffer.BlockCopy (_context, 0, data, 32, 8);
> +
> +			// target information
> +			data [40] = (byte) target_len;
> +			data [41] = (byte) (target_len >> 8);
> +			data [42] = data [40];
> +			data [43] = data [41];
> +			uint info_offset = (uint) (name_offset + name_bytes.Length);
> +			data [44] = (byte) info_offset;
> +			data [45] = (byte) (info_offset >> 8);
> +			data [46] = (byte) (info_offset >> 16);
> +			data [47] = (byte) (info_offset >> 24);
> +
> +			if (Version == NtlmVersion.Version3)
> +				Buffer.BlockCopy (OSVersion, 0, data, 48, OSVersion.Length);
> +
> +			Buffer.BlockCopy (name_bytes, 0, data, (int) name_offset, name_len);
> +			Buffer.BlockCopy (target, 0, data, (int) info_offset, target.Length);
> +
>  			return data;
>  		}
>  	}
> Index: Mono.Security.Protocol.Ntlm/Type3Message.cs
> ===================================================================
> --- Mono.Security.Protocol.Ntlm/Type3Message.cs	(revision 74928)
> +++ Mono.Security.Protocol.Ntlm/Type3Message.cs	(working copy)
> @@ -3,9 +3,10 @@
>  //
>  // Author:
>  //	Sebastien Pouliot <sebastien at ximian.com>
> +//	Atsushi Enomoto <atsushi at ximian.com>
>  //
>  // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
> -// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
> +// Copyright (C) 2004, 2007 Novell, Inc (http://www.novell.com)
>  //
>  // References
>  // a.	NTLM Authentication Scheme for HTTP, Ronald Tschalär
> @@ -35,6 +36,7 @@
>  
>  using System;
>  using System.Globalization;
> +using System.Security.Cryptography;
>  using System.Text;
>  
>  namespace Mono.Security.Protocol.Ntlm {
> @@ -48,18 +50,30 @@
>  		private string _password;
>  		private byte[] _lm;
>  		private byte[] _nt;
> +		private byte[] _nonce;
>  
> -		public Type3Message () : base (3)
> +		public Type3Message () : this (NtlmVersion.Version1)
>  		{
> +		}
> +
> +		public Type3Message (NtlmVersion version) : base (3, version)
> +		{
>  			// default values
>  			_domain = Environment.UserDomainName;
>  			_host = Environment.MachineName;
>  			_username = Environment.UserName;
>  			Flags = (NtlmFlags) 0x8201;
> +			_nonce = new byte [8];
> +			RandomNumberGenerator rng = RandomNumberGenerator.Create ();
> +			rng.GetBytes (_nonce);
>  		}
>  
> -		public Type3Message (byte[] message) : base (3)
> +		public Type3Message (byte[] message) : this (message, NtlmVersion.Version1)
>  		{
> +		}
> +
> +		public Type3Message (byte[] message, NtlmVersion version) : base (3, version)
> +		{
>  			Decode (message);
>  		}
>  
> @@ -125,15 +139,17 @@
>  		{
>  			base.Decode (message);
>  
> +/*
>  			if (BitConverterLE.ToUInt16 (message, 56) != message.Length) {
>  				string msg = Locale.GetText ("Invalid Type3 message length.");
>  				throw new ArgumentException (msg, "message");
>  			}
> +*/
>  
>  			_password = null;
>  
>  			int dom_len = BitConverterLE.ToUInt16 (message, 28);
> -			int dom_off = 64;
> +			int dom_off = BitConverterLE.ToUInt16 (message, 32);
>  			_domain = Encoding.Unicode.GetString (message, dom_off, dom_len);
>  
>  			int host_len = BitConverterLE.ToUInt16 (message, 44);
> @@ -162,60 +178,89 @@
>  			byte[] user = Encoding.Unicode.GetBytes (_username);
>  			byte[] host = Encoding.Unicode.GetBytes (_host.ToUpper (CultureInfo.InvariantCulture));
>  
> -			byte[] data = PrepareMessage (64 + domain.Length + user.Length + host.Length + 24 + 24);
> +			int fixed_size = Version == NtlmVersion.Version3 ? 72 : 64;
> +			short skey_len = (short) (Version != NtlmVersion.Version1 ? 16 : 0);
> +			int skey_off = 0;
>  
> +			byte[] data = PrepareMessage (fixed_size + domain.Length + user.Length + host.Length + 24 + 24 + skey_len);
> +
>  			// LM response
> -			short lmresp_off = (short)(64 + domain.Length + user.Length + host.Length);
> +			int lmresp_off = fixed_size + domain.Length + user.Length + host.Length;
>  			data [12] = (byte) 0x18;
>  			data [13] = (byte) 0x00;
>  			data [14] = (byte) 0x18;
>  			data [15] = (byte) 0x00;
>  			data [16] = (byte) lmresp_off;
>  			data [17] = (byte)(lmresp_off >> 8);
> +			data [18] = (byte)(lmresp_off >> 16);
> +			data [19] = (byte)(lmresp_off >> 24);
>  
>  			// NT response
> -			short ntresp_off = (short)(lmresp_off + 24);
> +			int ntresp_off = lmresp_off + 24;
>  			data [20] = (byte) 0x18;
>  			data [21] = (byte) 0x00;
>  			data [22] = (byte) 0x18;
>  			data [23] = (byte) 0x00;
>  			data [24] = (byte) ntresp_off;
>  			data [25] = (byte)(ntresp_off >> 8);
> +			data [26] = (byte)(ntresp_off >> 16);
> +			data [27] = (byte)(ntresp_off >> 24);
>  
>  			// domain
>  			short dom_len = (short)domain.Length;
> -			short dom_off = 64;
> +			int dom_off = (short)fixed_size;
>  			data [28] = (byte) dom_len;
>  			data [29] = (byte)(dom_len >> 8);
>  			data [30] = data [28];
>  			data [31] = data [29];
>  			data [32] = (byte) dom_off;
>  			data [33] = (byte)(dom_off >> 8);
> +			data [34] = (byte)(dom_off >> 16);
> +			data [35] = (byte)(dom_off >> 24);
>  
>  			// username
>  			short uname_len = (short)user.Length;
> -			short uname_off = (short)(dom_off + dom_len);
> +			int uname_off = dom_off + dom_len;
>  			data [36] = (byte) uname_len;
>  			data [37] = (byte)(uname_len >> 8);
>  			data [38] = data [36];
>  			data [39] = data [37];
>  			data [40] = (byte) uname_off;
>  			data [41] = (byte)(uname_off >> 8);
> +			data [42] = (byte)(uname_off >> 16);
> +			data [43] = (byte)(uname_off >> 24);
>  
>  			// host
>  			short host_len = (short)host.Length;
> -			short host_off = (short)(uname_off + uname_len);
> +			int host_off = uname_off + uname_len;
>  			data [44] = (byte) host_len;
>  			data [45] = (byte)(host_len >> 8);
>  			data [46] = data [44];
>  			data [47] = data [45];
>  			data [48] = (byte) host_off;
>  			data [49] = (byte)(host_off >> 8);
> +			data [50] = (byte)(host_off >> 16);
> +			data [51] = (byte)(host_off >> 24);
>  
> +			// session key
> +			if (Version != NtlmVersion.Version1) {
> +				skey_off = (short)(data.Length - skey_len);
> +				data [52] = (byte) skey_len;
> +				data [53] = (byte)(skey_len >> 8);
> +				data [54] = data [52];
> +				data [55] = data [53];
> +				data [56] = (byte) skey_off;
> +				data [57] = (byte)(skey_off >> 8);
> +				data [58] = (byte)(skey_off >> 16);
> +				data [59] = (byte)(skey_off >> 24);
> +			}
> +
> +/*
>  			// message length
>  			short msg_len = (short)data.Length;
>  			data [56] = (byte) msg_len;
>  			data [57] = (byte)(msg_len >> 8);
> +*/
>  
>  			// options flags
>  			data [60] = (byte) Flags;
> @@ -223,6 +268,10 @@
>  			data [62] = (byte)((uint)Flags >> 16);
>  			data [63] = (byte)((uint)Flags >> 24);
>  
> +			// osversion
> +			if (Version == NtlmVersion.Version3)
> +				Buffer.BlockCopy (OSVersion, 0, data, 64, OSVersion.Length);
> +
>  			Buffer.BlockCopy (domain, 0, data, dom_off, domain.Length);
>  			Buffer.BlockCopy (user, 0, data, uname_off, user.Length);
>  			Buffer.BlockCopy (host, 0, data, host_off, host.Length);
> @@ -230,7 +279,12 @@
>  			using (ChallengeResponse ntlm = new ChallengeResponse (_password, _challenge)) {
>  				Buffer.BlockCopy (ntlm.LM, 0, data, lmresp_off, 24);
>  				Buffer.BlockCopy (ntlm.NT, 0, data, ntresp_off, 24);
> +
> +				if (Version != NtlmVersion.Version1)
> +					// session key
> +					Buffer.BlockCopy (ntlm.LMSessionKey, 0, data, skey_off, 16);
>  			}
> +
>  			return data;
>  		}
>  	}
> Index: Mono.Security.dll.sources
> ===================================================================
> --- Mono.Security.dll.sources	(revision 74928)
> +++ Mono.Security.dll.sources	(working copy)
> @@ -68,6 +68,8 @@
>  ./Mono.Security.Protocol.Ntlm/ChallengeResponse.cs
>  ./Mono.Security.Protocol.Ntlm/MessageBase.cs
>  ./Mono.Security.Protocol.Ntlm/NtlmFlags.cs
> +./Mono.Security.Protocol.Ntlm/NtlmTargetInformation.cs
> +./Mono.Security.Protocol.Ntlm/NtlmVersion.cs
>  ./Mono.Security.Protocol.Ntlm/Type1Message.cs
>  ./Mono.Security.Protocol.Ntlm/Type2Message.cs
>  ./Mono.Security.Protocol.Ntlm/Type3Message.cs
> _______________________________________________
> Mono-devel-list mailing list
> Mono-devel-list at lists.ximian.com
> http://lists.ximian.com/mailman/listinfo/mono-devel-list




More information about the Mono-devel-list mailing list