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
{
47 class Type3Message
: MessageBase
{
49 private NtlmAuthLevel _level
;
50 private byte[] _challenge
;
52 private string _domain
;
53 private string _username
;
54 private string _password
;
55 private Type2Message _type2
;
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.");
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)
89 public Type3Message (Type2Message type2
) : base (3)
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
;
103 Flags
|= NtlmFlags
.NegotiateOem
;
105 if ((type2
.Flags
& NtlmFlags
.NegotiateNtlm2Key
) != 0)
106 Flags
|= NtlmFlags
.NegotiateNtlm2Key
;
111 if (_challenge
!= null)
112 Array
.Clear (_challenge
, 0, _challenge
.Length
);
114 Array
.Clear (_lm
, 0, _lm
.Length
);
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; }
135 [Obsolete (LegacyAPIWarning
)]
136 public byte[] Challenge
{
138 if (_challenge
== null)
140 return (byte[]) _challenge
.Clone (); }
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.");
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; }
163 Flags
&= ~NtlmFlags
.NegotiateDomainSupplied
;
165 Flags
|= NtlmFlags
.NegotiateDomainSupplied
;
172 get { return _host; }
177 Flags
&= ~NtlmFlags
.NegotiateWorkstationSupplied
;
179 Flags
|= NtlmFlags
.NegotiateWorkstationSupplied
;
185 public string Password
{
186 get { return _password; }
187 set { _password = value; }
190 public string Username
{
191 get { return _username; }
192 set { _username = value; }
206 protected override void Decode (byte[] message
)
208 base.Decode (message
);
212 if (message
.Length
>= 64)
213 Flags
= (NtlmFlags
)BitConverterLE
.ToUInt32 (message
, 60);
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
);
249 return Encoding
.ASCII
.GetString (buffer
, offset
, len
);
252 byte[] EncodeString (string text
)
256 if ((Flags
& NtlmFlags
.NegotiateUnicode
) != 0)
257 return Encoding
.Unicode
.GetBytes (text
);
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
);
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
)) {
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
);
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);
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);
307 short dom_len
= (short)target
.Length
;
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);
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);
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);
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
;
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
);
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
);