Merge pull request #2274 from esdrubal/udpclientreceive
[mono.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Ntlm / Type3Message.cs
1 //
2 // Mono.Security.Protocol.Ntlm.Type3Message - Authentication
3 //
4 // Author:
5 //      Sebastien Pouliot <sebastien@ximian.com>
6 //
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
9 //
10 // References
11 // a.   NTLM Authentication Scheme for HTTP, Ronald Tschalär
12 //      http://www.innovation.ch/java/ntlm.html
13 // b.   The NTLM Authentication Protocol, Copyright © 2003 Eric Glass
14 //      http://davenport.sourceforge.net/ntlm.html
15 //
16 // Permission is hereby granted, free of charge, to any person obtaining
17 // a copy of this software and associated documentation files (the
18 // "Software"), to deal in the Software without restriction, including
19 // without limitation the rights to use, copy, modify, merge, publish,
20 // distribute, sublicense, and/or sell copies of the Software, and to
21 // permit persons to whom the Software is furnished to do so, subject to
22 // the following conditions:
23 // 
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
26 // 
27 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
28 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
29 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
30 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
31 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
32 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
33 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 //
35
36 using System;
37 using System.Globalization;
38 using System.Text;
39
40 namespace Mono.Security.Protocol.Ntlm {
41
42 #if INSIDE_SYSTEM
43         internal
44 #else
45         public
46 #endif
47         class Type3Message : MessageBase {
48
49                 private NtlmAuthLevel _level;
50                 private byte[] _challenge;
51                 private string _host;
52                 private string _domain;
53                 private string _username;
54                 private string _password;
55                 private Type2Message _type2;
56                 private byte[] _lm;
57                 private byte[] _nt;
58                 
59                 internal const string LegacyAPIWarning = 
60                         "Use of this API is highly discouraged, " +
61                         "it selects legacy-mode LM/NTLM authentication, which sends " +
62                         "your password in very weak encryption over the wire even if " +
63                         "the server supports the more secure NTLMv2 / NTLMv2 Session. " +
64                         "You need to use the new `Type3Message (Type2Message)' constructor " +
65                         "to use the more secure NTLMv2 / NTLMv2 Session authentication modes. " +
66                         "These require the Type 2 message from the server to compute the response.";
67                 
68                 [Obsolete (LegacyAPIWarning)]
69                 public Type3Message () : base (3)
70                 {
71                         if (DefaultAuthLevel != NtlmAuthLevel.LM_and_NTLM)
72                                 throw new InvalidOperationException (
73                                         "Refusing to use legacy-mode LM/NTLM authentication " +
74                                         "unless explicitly enabled using DefaultAuthLevel.");
75
76                         // default values
77                         _domain = Environment.UserDomainName;
78                         _host = Environment.MachineName;
79                         _username = Environment.UserName;
80                         _level = NtlmAuthLevel.LM_and_NTLM;
81                         Flags = (NtlmFlags) 0x8201;
82                 }
83
84                 public Type3Message (byte[] message) : base (3)
85                 {
86                         Decode (message);
87                 }
88
89                 public Type3Message (Type2Message type2) : base (3)
90                 {
91                         _type2 = type2;
92                         _level = NtlmSettings.DefaultAuthLevel;
93                         _challenge = (byte[]) type2.Nonce.Clone ();
94
95                         _domain = type2.TargetName;
96                         _host = Environment.MachineName;
97                         _username = Environment.UserName;
98
99                         Flags = (NtlmFlags) 0x8200;
100                         if ((type2.Flags & NtlmFlags.NegotiateUnicode) != 0)
101                                 Flags |= NtlmFlags.NegotiateUnicode;
102                         else
103                                 Flags |= NtlmFlags.NegotiateOem;
104
105                         if ((type2.Flags & NtlmFlags.NegotiateNtlm2Key) != 0)
106                                 Flags |= NtlmFlags.NegotiateNtlm2Key;
107                 }
108
109                 ~Type3Message () 
110                 {
111                         if (_challenge != null)
112                                 Array.Clear (_challenge, 0, _challenge.Length);
113                         if (_lm != null)
114                                 Array.Clear (_lm, 0, _lm.Length);
115                         if (_nt != null)
116                                 Array.Clear (_nt, 0, _nt.Length);
117                 }
118
119                 // Default auth level
120                 [Obsolete ("Use NtlmSettings.DefaultAuthLevel")]
121                 public static NtlmAuthLevel DefaultAuthLevel {
122                         get { return NtlmSettings.DefaultAuthLevel; }
123                         set { NtlmSettings.DefaultAuthLevel = value; }
124                 }
125
126                 public NtlmAuthLevel Level {
127                         get { return _level; }
128                         set { _level = value; }
129                 }
130                 
131                 // properties
132
133                 [Obsolete (LegacyAPIWarning)]
134                 public byte[] Challenge {
135                         get { 
136                                 if (_challenge == null)
137                                         return null;
138                                 return (byte[]) _challenge.Clone (); }
139                         set { 
140                                 if ((_type2 != null) || (_level != NtlmAuthLevel.LM_and_NTLM))
141                                         throw new InvalidOperationException (
142                                                 "Refusing to use legacy-mode LM/NTLM authentication " +
143                                                         "unless explicitly enabled using DefaultAuthLevel.");
144                                 
145                                 if (value == null)
146                                         throw new ArgumentNullException ("Challenge");
147                                 if (value.Length != 8) {
148                                         string msg = Locale.GetText ("Invalid Challenge Length (should be 8 bytes).");
149                                         throw new ArgumentException (msg, "Challenge");
150                                 }
151                                 _challenge = (byte[]) value.Clone (); 
152                         }
153                 }
154
155                 public string Domain {
156                         get { return _domain; }
157                         set {
158                                 if (value == null)
159                                         value = "";
160                                 if (value == "")
161                                         Flags &= ~NtlmFlags.NegotiateDomainSupplied;
162                                 else
163                                         Flags |= NtlmFlags.NegotiateDomainSupplied;
164
165                                 _domain = value;
166                         }
167                 }
168
169                 public string Host {
170                         get { return _host; }
171                         set {
172                                 if (value == null)
173                                         value = "";
174                                 if (value == "")
175                                         Flags &= ~NtlmFlags.NegotiateWorkstationSupplied;
176                                 else
177                                         Flags |= NtlmFlags.NegotiateWorkstationSupplied;
178
179                                 _host = value;
180                         }
181                 }
182
183                 public string Password {
184                         get { return _password; }
185                         set { _password = value; }
186                 }
187
188                 public string Username {
189                         get { return _username; }
190                         set { _username = value; }
191                 }
192
193                 public byte[] LM {
194                         get { return _lm; }
195                 }
196
197                 public byte[] NT {
198                         get { return _nt; }
199                         set { _nt = value; }
200                 }
201
202                 // methods
203
204                 protected override void Decode (byte[] message)
205                 {
206                         base.Decode (message);
207
208                         _password = null;
209
210                         if (message.Length >= 64)
211                                 Flags = (NtlmFlags)BitConverterLE.ToUInt32 (message, 60);
212                         else
213                                 Flags = (NtlmFlags)0x8201;
214                         
215                         int lm_len = BitConverterLE.ToUInt16 (message, 12);
216                         int lm_off = BitConverterLE.ToUInt16 (message, 16);
217                         _lm = new byte [lm_len];
218                         Buffer.BlockCopy (message, lm_off, _lm, 0, lm_len);
219
220                         int nt_len = BitConverterLE.ToUInt16 (message, 20);
221                         int nt_off = BitConverterLE.ToUInt16 (message, 24);
222                         _nt = new byte [nt_len];
223                         Buffer.BlockCopy (message, nt_off, _nt, 0, nt_len);
224                         
225                         int dom_len = BitConverterLE.ToUInt16 (message, 28);
226                         int dom_off = BitConverterLE.ToUInt16 (message, 32);
227                         _domain = DecodeString (message, dom_off, dom_len);
228
229                         int user_len = BitConverterLE.ToUInt16 (message, 36);
230                         int user_off = BitConverterLE.ToUInt16 (message, 40);
231                         _username = DecodeString (message, user_off, user_len);
232                         
233                         int host_len = BitConverterLE.ToUInt16 (message, 44);
234                         int host_off = BitConverterLE.ToUInt16 (message, 48);
235                         _host = DecodeString (message, host_off, host_len);
236                         
237                         // Session key.  We don't use it yet.
238                         // int skey_len = BitConverterLE.ToUInt16 (message, 52);
239                         // int skey_off = BitConverterLE.ToUInt16 (message, 56);
240                 }
241
242                 string DecodeString (byte[] buffer, int offset, int len)
243                 {
244                         if ((Flags & NtlmFlags.NegotiateUnicode) != 0)
245                                 return Encoding.Unicode.GetString (buffer, offset, len);
246                         else
247                                 return Encoding.ASCII.GetString (buffer, offset, len);
248                 }
249
250                 byte[] EncodeString (string text)
251                 {
252                         if (text == null)
253                                 return new byte [0];
254                         if ((Flags & NtlmFlags.NegotiateUnicode) != 0)
255                                 return Encoding.Unicode.GetBytes (text);
256                         else
257                                 return Encoding.ASCII.GetBytes (text);
258                 }
259
260                 public override byte[] GetBytes ()
261                 {
262                         byte[] target = EncodeString (_domain);
263                         byte[] user = EncodeString (_username);
264                         byte[] host = EncodeString (_host);
265
266                         byte[] lm, ntlm;
267                         if (_type2 == null) {
268                                 if (_level != NtlmAuthLevel.LM_and_NTLM)
269                                         throw new InvalidOperationException (
270                                                 "Refusing to use legacy-mode LM/NTLM authentication " +
271                                                         "unless explicitly enabled using DefaultAuthLevel.");
272
273                                 #pragma warning disable 618
274                                 using (var legacy = new ChallengeResponse (_password, _challenge)) {
275                                         lm = legacy.LM;
276                                         ntlm = legacy.NT;
277                                 }
278                                 #pragma warning restore 618
279                         } else {
280                                 ChallengeResponse2.Compute (_type2, _level, _username, _password, _domain, out lm, out ntlm);
281                         }
282
283                         var lmresp_len = lm != null ? lm.Length : 0;
284                         var ntresp_len = ntlm != null ? ntlm.Length : 0;
285
286                         byte[] data = PrepareMessage (64 + target.Length + user.Length + host.Length + lmresp_len + ntresp_len);
287
288                         // LM response
289                         short lmresp_off = (short)(64 + target.Length + user.Length + host.Length);
290                         data [12] = (byte)lmresp_len;
291                         data [13] = (byte)0x00;
292                         data [14] = (byte)lmresp_len;
293                         data [15] = (byte)0x00;
294                         data [16] = (byte)lmresp_off;
295                         data [17] = (byte)(lmresp_off >> 8);
296
297                         // NT response
298                         short ntresp_off = (short)(lmresp_off + lmresp_len);
299                         data [20] = (byte)ntresp_len;
300                         data [21] = (byte)(ntresp_len >> 8);
301                         data [22] = (byte)ntresp_len;
302                         data [23] = (byte)(ntresp_len >> 8);
303                         data [24] = (byte)ntresp_off;
304                         data [25] = (byte)(ntresp_off >> 8);
305
306                         // target
307                         short dom_len = (short)target.Length;
308                         short dom_off = 64;
309                         data [28] = (byte)dom_len;
310                         data [29] = (byte)(dom_len >> 8);
311                         data [30] = data [28];
312                         data [31] = data [29];
313                         data [32] = (byte)dom_off;
314                         data [33] = (byte)(dom_off >> 8);
315
316                         // username
317                         short uname_len = (short)user.Length;
318                         short uname_off = (short)(dom_off + dom_len);
319                         data [36] = (byte)uname_len;
320                         data [37] = (byte)(uname_len >> 8);
321                         data [38] = data [36];
322                         data [39] = data [37];
323                         data [40] = (byte)uname_off;
324                         data [41] = (byte)(uname_off >> 8);
325
326                         // host
327                         short host_len = (short)host.Length;
328                         short host_off = (short)(uname_off + uname_len);
329                         data [44] = (byte)host_len;
330                         data [45] = (byte)(host_len >> 8);
331                         data [46] = data [44];
332                         data [47] = data [45];
333                         data [48] = (byte)host_off;
334                         data [49] = (byte)(host_off >> 8);
335
336                         // message length
337                         short msg_len = (short)data.Length;
338                         data [56] = (byte)msg_len;
339                         data [57] = (byte)(msg_len >> 8);
340
341                         int flags = (int)Flags;
342
343                         // options flags
344                         data [60] = (byte)flags;
345                         data [61] = (byte)((uint)flags >> 8);
346                         data [62] = (byte)((uint)flags >> 16);
347                         data [63] = (byte)((uint)flags >> 24);
348
349                         Buffer.BlockCopy (target, 0, data, dom_off, target.Length);
350                         Buffer.BlockCopy (user, 0, data, uname_off, user.Length);
351                         Buffer.BlockCopy (host, 0, data, host_off, host.Length);
352
353                         if (lm != null) {
354                                 Buffer.BlockCopy (lm, 0, data, lmresp_off, lm.Length);
355                                 Array.Clear (lm, 0, lm.Length);
356                         }
357                         Buffer.BlockCopy (ntlm, 0, data, ntresp_off, ntlm.Length);
358                         Array.Clear (ntlm, 0, ntlm.Length);
359
360                         return data;
361                 }
362         }
363 }