Implement NTLMv2 Session and NTLMv2 Authentication.
[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         public class Type3Message : MessageBase {
43
44                 private NtlmAuthLevel _level;
45                 private byte[] _challenge;
46                 private string _host;
47                 private string _domain;
48                 private string _username;
49                 private string _password;
50                 private Type2Message _type2;
51                 private byte[] _lm;
52                 private byte[] _nt;
53
54                 public Type3Message () : base (3)
55                 {
56                         // default values
57                         _domain = Environment.UserDomainName;
58                         _host = Environment.MachineName;
59                         _username = Environment.UserName;
60                         _level = NtlmAuthLevel.LM_and_NTLM;
61                         Flags = (NtlmFlags) 0x8201;
62                 }
63
64                 public Type3Message (byte[] message) : base (3)
65                 {
66                         Decode (message);
67                 }
68
69                 public Type3Message (Type2Message type2) : base (3)
70                 {
71                         _type2 = type2;
72                         _level = DefaultAuthLevel;
73                         _challenge = (byte[]) type2.Nonce.Clone ();
74
75                         _domain = type2.TargetName;
76                         _host = Environment.MachineName;
77                         _username = Environment.UserName;
78
79                         Flags = (NtlmFlags) 0x8200;
80                         if ((type2.Flags & NtlmFlags.NegotiateUnicode) != 0)
81                                 Flags |= NtlmFlags.NegotiateUnicode;
82                         else
83                                 Flags |= NtlmFlags.NegotiateOem;
84
85                         if ((type2.Flags & NtlmFlags.NegotiateNtlm2Key) != 0)
86                                 Flags |= NtlmFlags.NegotiateNtlm2Key;
87                 }
88
89                 ~Type3Message () 
90                 {
91                         if (_challenge != null)
92                                 Array.Clear (_challenge, 0, _challenge.Length);
93                         if (_lm != null)
94                                 Array.Clear (_lm, 0, _lm.Length);
95                         if (_nt != null)
96                                 Array.Clear (_nt, 0, _nt.Length);
97                 }
98
99                 // Default auth level
100
101                 static NtlmAuthLevel _default = NtlmAuthLevel.LM_and_NTLM_and_try_NTLMv2_Session;
102
103                 public static NtlmAuthLevel DefaultAuthLevel {
104                         get { return _default; }
105                         set { _default = value; }
106                 }
107
108                 public NtlmAuthLevel Level {
109                         get { return _level; }
110                         set { _level = value; }
111                 }
112                 
113                 // properties
114
115                 [Obsolete]
116                 public byte[] Challenge {
117                         get { 
118                                 if (_challenge == null)
119                                         return null;
120                                 return (byte[]) _challenge.Clone (); }
121                         set { 
122                                 if (value == null)
123                                         throw new ArgumentNullException ("Challenge");
124                                 if (value.Length != 8) {
125                                         string msg = Locale.GetText ("Invalid Challenge Length (should be 8 bytes).");
126                                         throw new ArgumentException (msg, "Challenge");
127                                 }
128                                 _challenge = (byte[]) value.Clone (); 
129                         }
130                 }
131
132                 public string Domain {
133                         get { return _domain; }
134                         set {
135                                 if (_type2 != null)
136                                         throw new InvalidOperationException (
137                                                 "Domain is set automatically from Type2Message.TargetName");
138                                 if (value == null)
139                                         value = "";
140                                 if (value == "")
141                                         Flags &= ~NtlmFlags.NegotiateDomainSupplied;
142                                 else
143                                         Flags |= NtlmFlags.NegotiateDomainSupplied;
144
145                                 _domain = value;
146                         }
147                 }
148
149                 public string Host {
150                         get { return _host; }
151                         set {
152                                 if (value == null)
153                                         value = "";
154                                 if (value == "")
155                                         Flags &= ~NtlmFlags.NegotiateWorkstationSupplied;
156                                 else
157                                         Flags |= NtlmFlags.NegotiateWorkstationSupplied;
158
159                                 _host = value;
160                         }
161                 }
162
163                 public string Password {
164                         get { return _password; }
165                         set { _password = value; }
166                 }
167
168                 public string Username {
169                         get { return _username; }
170                         set { _username = value; }
171                 }
172
173                 public byte[] LM {
174                         get { return _lm; }
175                 }
176
177                 public byte[] NT {
178                         get { return _nt; }
179                         set { _nt = value; }
180                 }
181
182                 // methods
183
184                 protected override void Decode (byte[] message)
185                 {
186                         base.Decode (message);
187
188                         if (BitConverterLE.ToUInt16 (message, 56) != message.Length) {
189                                 string msg = Locale.GetText ("Invalid Type3 message length.");
190                                 throw new ArgumentException (msg, "message");
191                         }
192
193                         _password = null;
194
195                         if (message.Length >= 64)
196                                 Flags = (NtlmFlags)BitConverterLE.ToUInt32 (message, 60);
197                         else
198                                 Flags = (NtlmFlags)0x8201;
199                         
200                         int lm_len = BitConverterLE.ToUInt16 (message, 12);
201                         int lm_off = BitConverterLE.ToUInt16 (message, 16);
202                         _lm = new byte [lm_len];
203                         Buffer.BlockCopy (message, lm_off, _lm, 0, lm_len);
204
205                         int nt_len = BitConverterLE.ToUInt16 (message, 20);
206                         int nt_off = BitConverterLE.ToUInt16 (message, 24);
207                         _nt = new byte [nt_len];
208                         Buffer.BlockCopy (message, nt_off, _nt, 0, nt_len);
209                         
210                         int dom_len = BitConverterLE.ToUInt16 (message, 28);
211                         int dom_off = BitConverterLE.ToUInt16 (message, 32);
212                         _domain = DecodeString (message, dom_off, dom_len);
213
214                         int user_len = BitConverterLE.ToUInt16 (message, 36);
215                         int user_off = BitConverterLE.ToUInt16 (message, 40);
216                         _username = DecodeString (message, user_off, user_len);
217                         
218                         int host_len = BitConverterLE.ToUInt16 (message, 44);
219                         int host_off = BitConverterLE.ToUInt16 (message, 48);
220                         _host = DecodeString (message, host_off, host_len);
221
222                         int skey_len = BitConverterLE.ToUInt16 (message, 52);
223                         int skey_off = BitConverterLE.ToUInt16 (message, 56);
224                 }
225
226                 string DecodeString (byte[] buffer, int offset, int len)
227                 {
228                         if ((Flags & NtlmFlags.NegotiateUnicode) != 0)
229                                 return Encoding.Unicode.GetString (buffer, offset, len);
230                         else
231                                 return Encoding.ASCII.GetString (buffer, offset, len);
232                 }
233
234                 byte[] EncodeString (string text)
235                 {
236                         if ((Flags & NtlmFlags.NegotiateUnicode) != 0)
237                                 return Encoding.Unicode.GetBytes (text);
238                         else
239                                 return Encoding.ASCII.GetBytes (text);
240                 }
241
242                 public override byte[] GetBytes ()
243                 {
244                         byte[] target = EncodeString (_domain);
245                         byte[] user = EncodeString (_username);
246                         byte[] host = EncodeString (_host);
247
248                         byte[] lm, ntlm;
249                         ChallengeResponse2.Compute (_type2, _level, _username, _password, out lm, out ntlm);
250
251                         var lmresp_len = lm != null ? lm.Length : 0;
252                         var ntresp_len = ntlm != null ? ntlm.Length : 0;
253
254                         byte[] data = PrepareMessage (64 + target.Length + user.Length + host.Length + lmresp_len + ntresp_len);
255
256                         // LM response
257                         short lmresp_off = (short)(64 + target.Length + user.Length + host.Length);
258                         data [12] = (byte)lmresp_len;
259                         data [13] = (byte)0x00;
260                         data [14] = (byte)lmresp_len;
261                         data [15] = (byte)0x00;
262                         data [16] = (byte)lmresp_off;
263                         data [17] = (byte)(lmresp_off >> 8);
264
265                         // NT response
266                         short ntresp_off = (short)(lmresp_off + lmresp_len);
267                         data [20] = (byte)ntresp_len;
268                         data [21] = (byte)(ntresp_len >> 8);
269                         data [22] = (byte)ntresp_len;
270                         data [23] = (byte)(ntresp_len >> 8);
271                         data [24] = (byte)ntresp_off;
272                         data [25] = (byte)(ntresp_off >> 8);
273
274                         // target
275                         short dom_len = (short)target.Length;
276                         short dom_off = 64;
277                         data [28] = (byte)dom_len;
278                         data [29] = (byte)(dom_len >> 8);
279                         data [30] = data [28];
280                         data [31] = data [29];
281                         data [32] = (byte)dom_off;
282                         data [33] = (byte)(dom_off >> 8);
283
284                         // username
285                         short uname_len = (short)user.Length;
286                         short uname_off = (short)(dom_off + dom_len);
287                         data [36] = (byte)uname_len;
288                         data [37] = (byte)(uname_len >> 8);
289                         data [38] = data [36];
290                         data [39] = data [37];
291                         data [40] = (byte)uname_off;
292                         data [41] = (byte)(uname_off >> 8);
293
294                         // host
295                         short host_len = (short)host.Length;
296                         short host_off = (short)(uname_off + uname_len);
297                         data [44] = (byte)host_len;
298                         data [45] = (byte)(host_len >> 8);
299                         data [46] = data [44];
300                         data [47] = data [45];
301                         data [48] = (byte)host_off;
302                         data [49] = (byte)(host_off >> 8);
303
304                         // message length
305                         short msg_len = (short)data.Length;
306                         data [56] = (byte)msg_len;
307                         data [57] = (byte)(msg_len >> 8);
308
309                         int flags = (int)Flags;
310
311                         // options flags
312                         data [60] = (byte)flags;
313                         data [61] = (byte)((uint)flags >> 8);
314                         data [62] = (byte)((uint)flags >> 16);
315                         data [63] = (byte)((uint)flags >> 24);
316
317                         Buffer.BlockCopy (target, 0, data, dom_off, target.Length);
318                         Buffer.BlockCopy (user, 0, data, uname_off, user.Length);
319                         Buffer.BlockCopy (host, 0, data, host_off, host.Length);
320
321                         if (lm != null) {
322                                 Buffer.BlockCopy (lm, 0, data, lmresp_off, lm.Length);
323                                 Array.Clear (lm, 0, lm.Length);
324                         }
325                         Buffer.BlockCopy (ntlm, 0, data, ntresp_off, ntlm.Length);
326                         Array.Clear (ntlm, 0, ntlm.Length);
327
328                         return data;
329                 }
330         }
331 }