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
120 [Obsolete ("Use NtlmSettings.DefaultAuthLevel")]
121 public static NtlmAuthLevel DefaultAuthLevel
{
122 get { return NtlmSettings.DefaultAuthLevel; }
123 set { NtlmSettings.DefaultAuthLevel = value; }
126 public NtlmAuthLevel Level
{
127 get { return _level; }
128 set { _level = value; }
133 [Obsolete (LegacyAPIWarning
)]
134 public byte[] Challenge
{
136 if (_challenge
== null)
138 return (byte[]) _challenge
.Clone (); }
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.");
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");
151 _challenge
= (byte[]) value.Clone ();
155 public string Domain
{
156 get { return _domain; }
161 Flags
&= ~NtlmFlags
.NegotiateDomainSupplied
;
163 Flags
|= NtlmFlags
.NegotiateDomainSupplied
;
170 get { return _host; }
175 Flags
&= ~NtlmFlags
.NegotiateWorkstationSupplied
;
177 Flags
|= NtlmFlags
.NegotiateWorkstationSupplied
;
183 public string Password
{
184 get { return _password; }
185 set { _password = value; }
188 public string Username
{
189 get { return _username; }
190 set { _username = value; }
204 protected override void Decode (byte[] message
)
206 base.Decode (message
);
210 if (message
.Length
>= 64)
211 Flags
= (NtlmFlags
)BitConverterLE
.ToUInt32 (message
, 60);
213 Flags
= (NtlmFlags
)0x8201;
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
);
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
);
225 int dom_len
= BitConverterLE
.ToUInt16 (message
, 28);
226 int dom_off
= BitConverterLE
.ToUInt16 (message
, 32);
227 _domain
= DecodeString (message
, dom_off
, dom_len
);
229 int user_len
= BitConverterLE
.ToUInt16 (message
, 36);
230 int user_off
= BitConverterLE
.ToUInt16 (message
, 40);
231 _username
= DecodeString (message
, user_off
, user_len
);
233 int host_len
= BitConverterLE
.ToUInt16 (message
, 44);
234 int host_off
= BitConverterLE
.ToUInt16 (message
, 48);
235 _host
= DecodeString (message
, host_off
, host_len
);
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);
242 string DecodeString (byte[] buffer
, int offset
, int len
)
244 if ((Flags
& NtlmFlags
.NegotiateUnicode
) != 0)
245 return Encoding
.Unicode
.GetString (buffer
, offset
, len
);
247 return Encoding
.ASCII
.GetString (buffer
, offset
, len
);
250 byte[] EncodeString (string text
)
254 if ((Flags
& NtlmFlags
.NegotiateUnicode
) != 0)
255 return Encoding
.Unicode
.GetBytes (text
);
257 return Encoding
.ASCII
.GetBytes (text
);
260 public override byte[] GetBytes ()
262 byte[] target
= EncodeString (_domain
);
263 byte[] user
= EncodeString (_username
);
264 byte[] host
= EncodeString (_host
);
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.");
273 using (var legacy
= new ChallengeResponse (_password
, _challenge
)) {
278 ChallengeResponse2
.Compute (_type2
, _level
, _username
, _password
, _domain
, out lm
, out ntlm
);
281 var lmresp_len
= lm
!= null ? lm
.Length
: 0;
282 var ntresp_len
= ntlm
!= null ? ntlm
.Length
: 0;
284 byte[] data
= PrepareMessage (64 + target
.Length
+ user
.Length
+ host
.Length
+ lmresp_len
+ ntresp_len
);
287 short lmresp_off
= (short)(64 + target
.Length
+ user
.Length
+ host
.Length
);
288 data
[12] = (byte)lmresp_len
;
289 data
[13] = (byte)0x00;
290 data
[14] = (byte)lmresp_len
;
291 data
[15] = (byte)0x00;
292 data
[16] = (byte)lmresp_off
;
293 data
[17] = (byte)(lmresp_off
>> 8);
296 short ntresp_off
= (short)(lmresp_off
+ lmresp_len
);
297 data
[20] = (byte)ntresp_len
;
298 data
[21] = (byte)(ntresp_len
>> 8);
299 data
[22] = (byte)ntresp_len
;
300 data
[23] = (byte)(ntresp_len
>> 8);
301 data
[24] = (byte)ntresp_off
;
302 data
[25] = (byte)(ntresp_off
>> 8);
305 short dom_len
= (short)target
.Length
;
307 data
[28] = (byte)dom_len
;
308 data
[29] = (byte)(dom_len
>> 8);
309 data
[30] = data
[28];
310 data
[31] = data
[29];
311 data
[32] = (byte)dom_off
;
312 data
[33] = (byte)(dom_off
>> 8);
315 short uname_len
= (short)user
.Length
;
316 short uname_off
= (short)(dom_off
+ dom_len
);
317 data
[36] = (byte)uname_len
;
318 data
[37] = (byte)(uname_len
>> 8);
319 data
[38] = data
[36];
320 data
[39] = data
[37];
321 data
[40] = (byte)uname_off
;
322 data
[41] = (byte)(uname_off
>> 8);
325 short host_len
= (short)host
.Length
;
326 short host_off
= (short)(uname_off
+ uname_len
);
327 data
[44] = (byte)host_len
;
328 data
[45] = (byte)(host_len
>> 8);
329 data
[46] = data
[44];
330 data
[47] = data
[45];
331 data
[48] = (byte)host_off
;
332 data
[49] = (byte)(host_off
>> 8);
335 short msg_len
= (short)data
.Length
;
336 data
[56] = (byte)msg_len
;
337 data
[57] = (byte)(msg_len
>> 8);
339 int flags
= (int)Flags
;
342 data
[60] = (byte)flags
;
343 data
[61] = (byte)((uint)flags
>> 8);
344 data
[62] = (byte)((uint)flags
>> 16);
345 data
[63] = (byte)((uint)flags
>> 24);
347 Buffer
.BlockCopy (target
, 0, data
, dom_off
, target
.Length
);
348 Buffer
.BlockCopy (user
, 0, data
, uname_off
, user
.Length
);
349 Buffer
.BlockCopy (host
, 0, data
, host_off
, host
.Length
);
352 Buffer
.BlockCopy (lm
, 0, data
, lmresp_off
, lm
.Length
);
353 Array
.Clear (lm
, 0, lm
.Length
);
355 Buffer
.BlockCopy (ntlm
, 0, data
, ntresp_off
, ntlm
.Length
);
356 Array
.Clear (ntlm
, 0, ntlm
.Length
);