Allow compiling SSL/TLS and NTLM support directly into System.dll to avoid a dependen...
[mono-project.git] / mcs / class / Mono.Security / Mono.Security.Protocol.Ntlm / Type3Message.cs
blobd80a50be699ed7494cb41f1536a51671c0e51722
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
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.
36 using System;
37 using System.Globalization;
38 using System.Text;
40 namespace Mono.Security.Protocol.Ntlm {
42 #if INSIDE_SYSTEM
43 internal
44 #else
45 public
46 #endif
47 class Type3Message : MessageBase {
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;
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.";
68 [Obsolete (LegacyAPIWarning)]
69 public Type3Message () : base (3)
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.");
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;
84 public Type3Message (byte[] message) : base (3)
86 Decode (message);
89 public Type3Message (Type2Message type2) : base (3)
91 _type2 = type2;
92 _level = DefaultAuthLevel;
93 _challenge = (byte[]) type2.Nonce.Clone ();
95 _domain = type2.TargetName;
96 _host = Environment.MachineName;
97 _username = Environment.UserName;
99 Flags = (NtlmFlags) 0x8200;
100 if ((type2.Flags & NtlmFlags.NegotiateUnicode) != 0)
101 Flags |= NtlmFlags.NegotiateUnicode;
102 else
103 Flags |= NtlmFlags.NegotiateOem;
105 if ((type2.Flags & NtlmFlags.NegotiateNtlm2Key) != 0)
106 Flags |= NtlmFlags.NegotiateNtlm2Key;
109 ~Type3Message ()
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);
119 // Default auth level
121 static NtlmAuthLevel _default = NtlmAuthLevel.LM_and_NTLM_and_try_NTLMv2_Session;
123 public static NtlmAuthLevel DefaultAuthLevel {
124 get { return _default; }
125 set { _default = value; }
128 public NtlmAuthLevel Level {
129 get { return _level; }
130 set { _level = value; }
133 // properties
135 [Obsolete (LegacyAPIWarning)]
136 public byte[] Challenge {
137 get {
138 if (_challenge == null)
139 return null;
140 return (byte[]) _challenge.Clone (); }
141 set {
142 if ((_type2 != null) || (_level != NtlmAuthLevel.LM_and_NTLM))
143 throw new InvalidOperationException (
144 "Refusing to use legacy-mode LM/NTLM authentication " +
145 "unless explicitly enabled using DefaultAuthLevel.");
147 if (value == null)
148 throw new ArgumentNullException ("Challenge");
149 if (value.Length != 8) {
150 string msg = Locale.GetText ("Invalid Challenge Length (should be 8 bytes).");
151 throw new ArgumentException (msg, "Challenge");
153 _challenge = (byte[]) value.Clone ();
157 public string Domain {
158 get { return _domain; }
159 set {
160 if (value == null)
161 value = "";
162 if (value == "")
163 Flags &= ~NtlmFlags.NegotiateDomainSupplied;
164 else
165 Flags |= NtlmFlags.NegotiateDomainSupplied;
167 _domain = value;
171 public string Host {
172 get { return _host; }
173 set {
174 if (value == null)
175 value = "";
176 if (value == "")
177 Flags &= ~NtlmFlags.NegotiateWorkstationSupplied;
178 else
179 Flags |= NtlmFlags.NegotiateWorkstationSupplied;
181 _host = value;
185 public string Password {
186 get { return _password; }
187 set { _password = value; }
190 public string Username {
191 get { return _username; }
192 set { _username = value; }
195 public byte[] LM {
196 get { return _lm; }
199 public byte[] NT {
200 get { return _nt; }
201 set { _nt = value; }
204 // methods
206 protected override void Decode (byte[] message)
208 base.Decode (message);
210 _password = null;
212 if (message.Length >= 64)
213 Flags = (NtlmFlags)BitConverterLE.ToUInt32 (message, 60);
214 else
215 Flags = (NtlmFlags)0x8201;
217 int lm_len = BitConverterLE.ToUInt16 (message, 12);
218 int lm_off = BitConverterLE.ToUInt16 (message, 16);
219 _lm = new byte [lm_len];
220 Buffer.BlockCopy (message, lm_off, _lm, 0, lm_len);
222 int nt_len = BitConverterLE.ToUInt16 (message, 20);
223 int nt_off = BitConverterLE.ToUInt16 (message, 24);
224 _nt = new byte [nt_len];
225 Buffer.BlockCopy (message, nt_off, _nt, 0, nt_len);
227 int dom_len = BitConverterLE.ToUInt16 (message, 28);
228 int dom_off = BitConverterLE.ToUInt16 (message, 32);
229 _domain = DecodeString (message, dom_off, dom_len);
231 int user_len = BitConverterLE.ToUInt16 (message, 36);
232 int user_off = BitConverterLE.ToUInt16 (message, 40);
233 _username = DecodeString (message, user_off, user_len);
235 int host_len = BitConverterLE.ToUInt16 (message, 44);
236 int host_off = BitConverterLE.ToUInt16 (message, 48);
237 _host = DecodeString (message, host_off, host_len);
239 // Session key. We don't use it yet.
240 // int skey_len = BitConverterLE.ToUInt16 (message, 52);
241 // int skey_off = BitConverterLE.ToUInt16 (message, 56);
244 string DecodeString (byte[] buffer, int offset, int len)
246 if ((Flags & NtlmFlags.NegotiateUnicode) != 0)
247 return Encoding.Unicode.GetString (buffer, offset, len);
248 else
249 return Encoding.ASCII.GetString (buffer, offset, len);
252 byte[] EncodeString (string text)
254 if (text == null)
255 return new byte [0];
256 if ((Flags & NtlmFlags.NegotiateUnicode) != 0)
257 return Encoding.Unicode.GetBytes (text);
258 else
259 return Encoding.ASCII.GetBytes (text);
262 public override byte[] GetBytes ()
264 byte[] target = EncodeString (_domain);
265 byte[] user = EncodeString (_username);
266 byte[] host = EncodeString (_host);
268 byte[] lm, ntlm;
269 if (_type2 == null) {
270 if (_level != NtlmAuthLevel.LM_and_NTLM)
271 throw new InvalidOperationException (
272 "Refusing to use legacy-mode LM/NTLM authentication " +
273 "unless explicitly enabled using DefaultAuthLevel.");
275 using (var legacy = new ChallengeResponse (_password, _challenge)) {
276 lm = legacy.LM;
277 ntlm = legacy.NT;
279 } else {
280 ChallengeResponse2.Compute (_type2, _level, _username, _password, _domain, out lm, out ntlm);
283 var lmresp_len = lm != null ? lm.Length : 0;
284 var ntresp_len = ntlm != null ? ntlm.Length : 0;
286 byte[] data = PrepareMessage (64 + target.Length + user.Length + host.Length + lmresp_len + ntresp_len);
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);
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);
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);
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);
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);
336 // message length
337 short msg_len = (short)data.Length;
338 data [56] = (byte)msg_len;
339 data [57] = (byte)(msg_len >> 8);
341 int flags = (int)Flags;
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);
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);
353 if (lm != null) {
354 Buffer.BlockCopy (lm, 0, data, lmresp_off, lm.Length);
355 Array.Clear (lm, 0, lm.Length);
357 Buffer.BlockCopy (ntlm, 0, data, ntresp_off, ntlm.Length);
358 Array.Clear (ntlm, 0, ntlm.Length);
360 return data;