Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Ntlm / Type3Message.cs
index 198907861e5a0f31edcc046778f429e7b575b8e4..59e948fdfd9b5e0d77f14eb86a61dca34d345709 100644 (file)
@@ -41,20 +41,38 @@ namespace Mono.Security.Protocol.Ntlm {
 
        public class Type3Message : MessageBase {
 
+               private NtlmAuthLevel _level;
                private byte[] _challenge;
                private string _host;
                private string _domain;
                private string _username;
                private string _password;
+               private Type2Message _type2;
                private byte[] _lm;
                private byte[] _nt;
-
+               
+               internal const string LegacyAPIWarning = 
+                       "Use of this API is highly discouraged, " +
+                       "it selects legacy-mode LM/NTLM authentication, which sends " +
+                       "your password in very weak encryption over the wire even if " +
+                       "the server supports the more secure NTLMv2 / NTLMv2 Session. " +
+                       "You need to use the new `Type3Message (Type2Message)' constructor " +
+                       "to use the more secure NTLMv2 / NTLMv2 Session authentication modes. " +
+                       "These require the Type 2 message from the server to compute the response.";
+               
+               [Obsolete (LegacyAPIWarning)]
                public Type3Message () : base (3)
                {
+                       if (DefaultAuthLevel != NtlmAuthLevel.LM_and_NTLM)
+                               throw new InvalidOperationException (
+                                       "Refusing to use legacy-mode LM/NTLM authentication " +
+                                       "unless explicitly enabled using DefaultAuthLevel.");
+
                        // default values
                        _domain = Environment.UserDomainName;
                        _host = Environment.MachineName;
                        _username = Environment.UserName;
+                       _level = NtlmAuthLevel.LM_and_NTLM;
                        Flags = (NtlmFlags) 0x8201;
                }
 
@@ -63,6 +81,26 @@ namespace Mono.Security.Protocol.Ntlm {
                        Decode (message);
                }
 
+               public Type3Message (Type2Message type2) : base (3)
+               {
+                       _type2 = type2;
+                       _level = DefaultAuthLevel;
+                       _challenge = (byte[]) type2.Nonce.Clone ();
+
+                       _domain = type2.TargetName;
+                       _host = Environment.MachineName;
+                       _username = Environment.UserName;
+
+                       Flags = (NtlmFlags) 0x8200;
+                       if ((type2.Flags & NtlmFlags.NegotiateUnicode) != 0)
+                               Flags |= NtlmFlags.NegotiateUnicode;
+                       else
+                               Flags |= NtlmFlags.NegotiateOem;
+
+                       if ((type2.Flags & NtlmFlags.NegotiateNtlm2Key) != 0)
+                               Flags |= NtlmFlags.NegotiateNtlm2Key;
+               }
+
                ~Type3Message () 
                {
                        if (_challenge != null)
@@ -73,14 +111,34 @@ namespace Mono.Security.Protocol.Ntlm {
                                Array.Clear (_nt, 0, _nt.Length);
                }
 
+               // Default auth level
+
+               static NtlmAuthLevel _default = NtlmAuthLevel.LM_and_NTLM_and_try_NTLMv2_Session;
+
+               public static NtlmAuthLevel DefaultAuthLevel {
+                       get { return _default; }
+                       set { _default = value; }
+               }
+
+               public NtlmAuthLevel Level {
+                       get { return _level; }
+                       set { _level = value; }
+               }
+               
                // properties
 
+               [Obsolete (LegacyAPIWarning)]
                public byte[] Challenge {
                        get { 
                                if (_challenge == null)
                                        return null;
                                return (byte[]) _challenge.Clone (); }
                        set { 
+                               if ((_type2 != null) || (_level != NtlmAuthLevel.LM_and_NTLM))
+                                       throw new InvalidOperationException (
+                                               "Refusing to use legacy-mode LM/NTLM authentication " +
+                                                       "unless explicitly enabled using DefaultAuthLevel.");
+                               
                                if (value == null)
                                        throw new ArgumentNullException ("Challenge");
                                if (value.Length != 8) {
@@ -93,12 +151,33 @@ namespace Mono.Security.Protocol.Ntlm {
 
                public string Domain {
                        get { return _domain; }
-                       set { _domain = value; }
+                       set {
+                               if (_type2 != null)
+                                       throw new InvalidOperationException (
+                                               "Domain is set automatically from Type2Message.TargetName");
+                               if (value == null)
+                                       value = "";
+                               if (value == "")
+                                       Flags &= ~NtlmFlags.NegotiateDomainSupplied;
+                               else
+                                       Flags |= NtlmFlags.NegotiateDomainSupplied;
+
+                               _domain = value;
+                       }
                }
 
                public string Host {
                        get { return _host; }
-                       set { _host = value; }
+                       set {
+                               if (value == null)
+                                       value = "";
+                               if (value == "")
+                                       Flags &= ~NtlmFlags.NegotiateWorkstationSupplied;
+                               else
+                                       Flags |= NtlmFlags.NegotiateWorkstationSupplied;
+
+                               _host = value;
+                       }
                }
 
                public string Password {
@@ -117,11 +196,12 @@ namespace Mono.Security.Protocol.Ntlm {
 
                public byte[] NT {
                        get { return _nt; }
+                       set { _nt = value; }
                }
 
                // methods
 
-               protected override void Decode (byte[] message) 
+               protected override void Decode (byte[] message)
                {
                        base.Decode (message);
 
@@ -132,105 +212,154 @@ namespace Mono.Security.Protocol.Ntlm {
 
                        _password = null;
 
-                       int dom_len = BitConverterLE.ToUInt16 (message, 28);
-                       int dom_off = 64;
-                       _domain = Encoding.Unicode.GetString (message, dom_off, dom_len);
+                       if (message.Length >= 64)
+                               Flags = (NtlmFlags)BitConverterLE.ToUInt32 (message, 60);
+                       else
+                               Flags = (NtlmFlags)0x8201;
+                       
+                       int lm_len = BitConverterLE.ToUInt16 (message, 12);
+                       int lm_off = BitConverterLE.ToUInt16 (message, 16);
+                       _lm = new byte [lm_len];
+                       Buffer.BlockCopy (message, lm_off, _lm, 0, lm_len);
 
-                       int host_len = BitConverterLE.ToUInt16 (message, 44);
-                       int host_off = BitConverterLE.ToUInt16 (message, 48);
-                       _host = Encoding.Unicode.GetString (message, host_off, host_len);
+                       int nt_len = BitConverterLE.ToUInt16 (message, 20);
+                       int nt_off = BitConverterLE.ToUInt16 (message, 24);
+                       _nt = new byte [nt_len];
+                       Buffer.BlockCopy (message, nt_off, _nt, 0, nt_len);
+                       
+                       int dom_len = BitConverterLE.ToUInt16 (message, 28);
+                       int dom_off = BitConverterLE.ToUInt16 (message, 32);
+                       _domain = DecodeString (message, dom_off, dom_len);
 
                        int user_len = BitConverterLE.ToUInt16 (message, 36);
                        int user_off = BitConverterLE.ToUInt16 (message, 40);
-                       _username = Encoding.Unicode.GetString (message, user_off, user_len);
-
-                       _lm = new byte [24];
-                       int lm_off = BitConverterLE.ToUInt16 (message, 16);
-                       Buffer.BlockCopy (message, lm_off, _lm, 0, 24);
+                       _username = DecodeString (message, user_off, user_len);
                        
-                       _nt = new byte [24];
-                       int nt_off = BitConverterLE.ToUInt16 (message, 24);
-                       Buffer.BlockCopy (message, nt_off, _nt, 0, 24);
+                       int host_len = BitConverterLE.ToUInt16 (message, 44);
+                       int host_off = BitConverterLE.ToUInt16 (message, 48);
+                       _host = DecodeString (message, host_off, host_len);
+                       
+                       // Session key.  We don't use it yet.
+                       // int skey_len = BitConverterLE.ToUInt16 (message, 52);
+                       // int skey_off = BitConverterLE.ToUInt16 (message, 56);
+               }
 
-                       if (message.Length >= 64)
-                               Flags = (NtlmFlags) BitConverterLE.ToUInt32 (message, 60);
+               string DecodeString (byte[] buffer, int offset, int len)
+               {
+                       if ((Flags & NtlmFlags.NegotiateUnicode) != 0)
+                               return Encoding.Unicode.GetString (buffer, offset, len);
+                       else
+                               return Encoding.ASCII.GetString (buffer, offset, len);
                }
 
-               public override byte[] GetBytes () 
+               byte[] EncodeString (string text)
                {
-                       byte[] domain = Encoding.Unicode.GetBytes (_domain.ToUpper (CultureInfo.InvariantCulture));
-                       byte[] user = Encoding.Unicode.GetBytes (_username);
-                       byte[] host = Encoding.Unicode.GetBytes (_host.ToUpper (CultureInfo.InvariantCulture));
+                       if (text == null)
+                               return new byte [0];
+                       if ((Flags & NtlmFlags.NegotiateUnicode) != 0)
+                               return Encoding.Unicode.GetBytes (text);
+                       else
+                               return Encoding.ASCII.GetBytes (text);
+               }
 
-                       byte[] data = PrepareMessage (64 + domain.Length + user.Length + host.Length + 24 + 24);
+               public override byte[] GetBytes ()
+               {
+                       byte[] target = EncodeString (_domain);
+                       byte[] user = EncodeString (_username);
+                       byte[] host = EncodeString (_host);
+
+                       byte[] lm, ntlm;
+                       if (_type2 == null) {
+                               if (_level != NtlmAuthLevel.LM_and_NTLM)
+                                       throw new InvalidOperationException (
+                                               "Refusing to use legacy-mode LM/NTLM authentication " +
+                                                       "unless explicitly enabled using DefaultAuthLevel.");
+                               
+                               using (var legacy = new ChallengeResponse (_password, _challenge)) {
+                                       lm = legacy.LM;
+                                       ntlm = legacy.NT;
+                               }
+                       } else {
+                               ChallengeResponse2.Compute (_type2, _level, _username, _password, out lm, out ntlm);
+                       }
+
+                       var lmresp_len = lm != null ? lm.Length : 0;
+                       var ntresp_len = ntlm != null ? ntlm.Length : 0;
+
+                       byte[] data = PrepareMessage (64 + target.Length + user.Length + host.Length + lmresp_len + ntresp_len);
 
                        // LM response
-                       short lmresp_off = (short)(64 + 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;
+                       short lmresp_off = (short)(64 + target.Length + user.Length + host.Length);
+                       data [12] = (byte)lmresp_len;
+                       data [13] = (byte)0x00;
+                       data [14] = (byte)lmresp_len;
+                       data [15] = (byte)0x00;
+                       data [16] = (byte)lmresp_off;
                        data [17] = (byte)(lmresp_off >> 8);
 
                        // NT response
-                       short ntresp_off = (short)(lmresp_off + 24);
-                       data [20] = (byte) 0x18;
-                       data [21] = (byte) 0x00;
-                       data [22] = (byte) 0x18;
-                       data [23] = (byte) 0x00;
-                       data [24] = (byte) ntresp_off;
+                       short ntresp_off = (short)(lmresp_off + lmresp_len);
+                       data [20] = (byte)ntresp_len;
+                       data [21] = (byte)(ntresp_len >> 8);
+                       data [22] = (byte)ntresp_len;
+                       data [23] = (byte)(ntresp_len >> 8);
+                       data [24] = (byte)ntresp_off;
                        data [25] = (byte)(ntresp_off >> 8);
 
-                       // domain
-                       short dom_len = (short)domain.Length;
+                       // target
+                       short dom_len = (short)target.Length;
                        short dom_off = 64;
-                       data [28] = (byte) dom_len;
+                       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 [32] = (byte)dom_off;
                        data [33] = (byte)(dom_off >> 8);
 
                        // username
                        short uname_len = (short)user.Length;
                        short uname_off = (short)(dom_off + dom_len);
-                       data [36] = (byte) uname_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 [40] = (byte)uname_off;
                        data [41] = (byte)(uname_off >> 8);
 
                        // host
                        short host_len = (short)host.Length;
                        short host_off = (short)(uname_off + uname_len);
-                       data [44] = (byte) host_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 [48] = (byte)host_off;
                        data [49] = (byte)(host_off >> 8);
 
                        // message length
                        short msg_len = (short)data.Length;
-                       data [56] = (byte) msg_len;
+                       data [56] = (byte)msg_len;
                        data [57] = (byte)(msg_len >> 8);
 
+                       int flags = (int)Flags;
+
                        // options flags
-                       data [60] = (byte) Flags;
-                       data [61] = (byte)((uint)Flags >> 8);
-                       data [62] = (byte)((uint)Flags >> 16);
-                       data [63] = (byte)((uint)Flags >> 24);
+                       data [60] = (byte)flags;
+                       data [61] = (byte)((uint)flags >> 8);
+                       data [62] = (byte)((uint)flags >> 16);
+                       data [63] = (byte)((uint)flags >> 24);
 
-                       Buffer.BlockCopy (domain, 0, data, dom_off, domain.Length);
+                       Buffer.BlockCopy (target, 0, data, dom_off, target.Length);
                        Buffer.BlockCopy (user, 0, data, uname_off, user.Length);
                        Buffer.BlockCopy (host, 0, data, host_off, host.Length);
 
-                       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 (lm != null) {
+                               Buffer.BlockCopy (lm, 0, data, lmresp_off, lm.Length);
+                               Array.Clear (lm, 0, lm.Length);
                        }
+                       Buffer.BlockCopy (ntlm, 0, data, ntresp_off, ntlm.Length);
+                       Array.Clear (ntlm, 0, ntlm.Length);
+
                        return data;
                }
        }