2 // Mono.Security.Protocol.Ntlm.Type3Message - Authentication
5 // Sebastien Pouliot <sebastien@ximian.com>
7 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
8 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
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
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:
24 // The above copyright notice and this permission notice shall be
25 // included in all copies or substantial portions of the Software.
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.
37 using System.Globalization;
40 namespace Mono.Security.Protocol.Ntlm {
42 public class Type3Message : MessageBase {
44 private NtlmAuthLevel _level;
45 private byte[] _challenge;
47 private string _domain;
48 private string _username;
49 private string _password;
50 private Type2Message _type2;
54 public Type3Message () : base (3)
57 _domain = Environment.UserDomainName;
58 _host = Environment.MachineName;
59 _username = Environment.UserName;
60 _level = NtlmAuthLevel.LM_and_NTLM;
61 Flags = (NtlmFlags) 0x8201;
64 public Type3Message (byte[] message) : base (3)
69 public Type3Message (Type2Message type2) : base (3)
72 _level = DefaultAuthLevel;
73 _challenge = (byte[]) type2.Nonce.Clone ();
75 _domain = type2.TargetName;
76 _host = Environment.MachineName;
77 _username = Environment.UserName;
79 Flags = (NtlmFlags) 0x8200;
80 if ((type2.Flags & NtlmFlags.NegotiateUnicode) != 0)
81 Flags |= NtlmFlags.NegotiateUnicode;
83 Flags |= NtlmFlags.NegotiateOem;
85 if ((type2.Flags & NtlmFlags.NegotiateNtlm2Key) != 0)
86 Flags |= NtlmFlags.NegotiateNtlm2Key;
91 if (_challenge != null)
92 Array.Clear (_challenge, 0, _challenge.Length);
94 Array.Clear (_lm, 0, _lm.Length);
96 Array.Clear (_nt, 0, _nt.Length);
101 static NtlmAuthLevel _default = NtlmAuthLevel.LM_and_NTLM_and_try_NTLMv2_Session;
103 public static NtlmAuthLevel DefaultAuthLevel {
104 get { return _default; }
105 set { _default = value; }
108 public NtlmAuthLevel Level {
109 get { return _level; }
110 set { _level = value; }
116 public byte[] Challenge {
118 if (_challenge == null)
120 return (byte[]) _challenge.Clone (); }
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");
128 _challenge = (byte[]) value.Clone ();
132 public string Domain {
133 get { return _domain; }
136 throw new InvalidOperationException (
137 "Domain is set automatically from Type2Message.TargetName");
141 Flags &= ~NtlmFlags.NegotiateDomainSupplied;
143 Flags |= NtlmFlags.NegotiateDomainSupplied;
150 get { return _host; }
155 Flags &= ~NtlmFlags.NegotiateWorkstationSupplied;
157 Flags |= NtlmFlags.NegotiateWorkstationSupplied;
163 public string Password {
164 get { return _password; }
165 set { _password = value; }
168 public string Username {
169 get { return _username; }
170 set { _username = value; }
184 protected override void Decode (byte[] message)
186 base.Decode (message);
188 if (BitConverterLE.ToUInt16 (message, 56) != message.Length) {
189 string msg = Locale.GetText ("Invalid Type3 message length.");
190 throw new ArgumentException (msg, "message");
195 if (message.Length >= 64)
196 Flags = (NtlmFlags)BitConverterLE.ToUInt32 (message, 60);
198 Flags = (NtlmFlags)0x8201;
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);
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);
210 int dom_len = BitConverterLE.ToUInt16 (message, 28);
211 int dom_off = BitConverterLE.ToUInt16 (message, 32);
212 _domain = DecodeString (message, dom_off, dom_len);
214 int user_len = BitConverterLE.ToUInt16 (message, 36);
215 int user_off = BitConverterLE.ToUInt16 (message, 40);
216 _username = DecodeString (message, user_off, user_len);
218 int host_len = BitConverterLE.ToUInt16 (message, 44);
219 int host_off = BitConverterLE.ToUInt16 (message, 48);
220 _host = DecodeString (message, host_off, host_len);
222 int skey_len = BitConverterLE.ToUInt16 (message, 52);
223 int skey_off = BitConverterLE.ToUInt16 (message, 56);
226 string DecodeString (byte[] buffer, int offset, int len)
228 if ((Flags & NtlmFlags.NegotiateUnicode) != 0)
229 return Encoding.Unicode.GetString (buffer, offset, len);
231 return Encoding.ASCII.GetString (buffer, offset, len);
234 byte[] EncodeString (string text)
236 if ((Flags & NtlmFlags.NegotiateUnicode) != 0)
237 return Encoding.Unicode.GetBytes (text);
239 return Encoding.ASCII.GetBytes (text);
242 public override byte[] GetBytes ()
244 byte[] target = EncodeString (_domain);
245 byte[] user = EncodeString (_username);
246 byte[] host = EncodeString (_host);
249 ChallengeResponse2.Compute (_type2, _level, _username, _password, out lm, out ntlm);
251 var lmresp_len = lm != null ? lm.Length : 0;
252 var ntresp_len = ntlm != null ? ntlm.Length : 0;
254 byte[] data = PrepareMessage (64 + target.Length + user.Length + host.Length + lmresp_len + ntresp_len);
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);
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);
275 short dom_len = (short)target.Length;
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);
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);
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);
305 short msg_len = (short)data.Length;
306 data [56] = (byte)msg_len;
307 data [57] = (byte)(msg_len >> 8);
309 int flags = (int)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);
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);
322 Buffer.BlockCopy (lm, 0, data, lmresp_off, lm.Length);
323 Array.Clear (lm, 0, lm.Length);
325 Buffer.BlockCopy (ntlm, 0, data, ntresp_off, ntlm.Length);
326 Array.Clear (ntlm, 0, ntlm.Length);