diff options
Diffstat (limited to 'Emby.Common.Implementations/IO/SharpCifs/Smb/NtlmPasswordAuthentication.cs')
| -rw-r--r-- | Emby.Common.Implementations/IO/SharpCifs/Smb/NtlmPasswordAuthentication.cs | 1614 |
1 files changed, 830 insertions, 784 deletions
diff --git a/Emby.Common.Implementations/IO/SharpCifs/Smb/NtlmPasswordAuthentication.cs b/Emby.Common.Implementations/IO/SharpCifs/Smb/NtlmPasswordAuthentication.cs index ec3899fe9..7f25f5efc 100644 --- a/Emby.Common.Implementations/IO/SharpCifs/Smb/NtlmPasswordAuthentication.cs +++ b/Emby.Common.Implementations/IO/SharpCifs/Smb/NtlmPasswordAuthentication.cs @@ -20,788 +20,834 @@ using SharpCifs.Util.Sharpen; namespace SharpCifs.Smb { - /// <summary>This class stores and encrypts NTLM user credentials.</summary> - /// <remarks> - /// This class stores and encrypts NTLM user credentials. The default - /// credentials are retrieved from the <tt>jcifs.smb.client.domain</tt>, - /// <tt>jcifs.smb.client.username</tt>, and <tt>jcifs.smb.client.password</tt> - /// properties. - /// <p> - /// Read <a href="../../../authhandler.html">jCIFS Exceptions and - /// NtlmAuthenticator</a> for related information. - /// </remarks> - - public sealed class NtlmPasswordAuthentication : Principal - { - private static readonly int LmCompatibility = Config.GetInt("jcifs.smb.lmCompatibility" - , 3); - - private static readonly Random Random = new Random(); - - private static LogStream _log = LogStream.GetInstance(); - - private static readonly byte[] S8 = { unchecked(unchecked(0x4b)), unchecked(unchecked(0x47)), unchecked(unchecked(0x53)), unchecked(unchecked(0x21)), unchecked(unchecked(0x40)), unchecked(unchecked(0x23)), unchecked(unchecked(0x24)), unchecked(unchecked(0x25)) }; - - // KGS!@#$% - private static void E(byte[] key, byte[] data, byte[] e) - { - byte[] key7 = new byte[7]; - byte[] e8 = new byte[8]; - for (int i = 0; i < key.Length / 7; i++) - { - Array.Copy(key, i * 7, key7, 0, 7); - DES des = new DES(key7); - des.Encrypt(data, e8); - Array.Copy(e8, 0, e, i * 8, 8); - } - } - - internal static string DefaultDomain; - - internal static string DefaultUsername; - - internal static string DefaultPassword; - - internal static readonly string Blank = string.Empty; - - public static readonly NtlmPasswordAuthentication Anonymous = new NtlmPasswordAuthentication - (string.Empty, string.Empty, string.Empty); - - internal static void InitDefaults() - { - if (DefaultDomain != null) - { - return; - } - DefaultDomain = Config.GetProperty("jcifs.smb.client.domain", "?"); - DefaultUsername = Config.GetProperty("jcifs.smb.client.username", "GUEST"); - DefaultPassword = Config.GetProperty("jcifs.smb.client.password", Blank); - } - - /// <summary>Generate the ANSI DES hash for the password associated with these credentials. - /// </summary> - /// <remarks>Generate the ANSI DES hash for the password associated with these credentials. - /// </remarks> - public static byte[] GetPreNtlmResponse(string password, byte[] challenge) - { - byte[] p14 = new byte[14]; - byte[] p21 = new byte[21]; - byte[] p24 = new byte[24]; - byte[] passwordBytes; - try - { - passwordBytes = Runtime.GetBytesForString(password.ToUpper(), SmbConstants.OemEncoding); - } - catch (UnsupportedEncodingException uee) - { - throw new RuntimeException("Try setting jcifs.encoding=US-ASCII", uee); - } - int passwordLength = passwordBytes.Length; - // Only encrypt the first 14 bytes of the password for Pre 0.12 NT LM - if (passwordLength > 14) - { - passwordLength = 14; - } - Array.Copy(passwordBytes, 0, p14, 0, passwordLength); - E(p14, S8, p21); - E(p21, challenge, p24); - return p24; - } - - /// <summary>Generate the Unicode MD4 hash for the password associated with these credentials. - /// </summary> - /// <remarks>Generate the Unicode MD4 hash for the password associated with these credentials. - /// </remarks> - public static byte[] GetNtlmResponse(string password, byte[] challenge) - { - byte[] uni = null; - byte[] p21 = new byte[21]; - byte[] p24 = new byte[24]; - try - { - uni = Runtime.GetBytesForString(password, SmbConstants.UniEncoding); - } - catch (UnsupportedEncodingException uee) - { - if (_log.Level > 0) - { - Runtime.PrintStackTrace(uee, _log); - } - } - Md4 md4 = new Md4(); - md4.Update(uni); - try - { - md4.Digest(p21, 0, 16); - } - catch (Exception ex) - { - if (_log.Level > 0) - { - Runtime.PrintStackTrace(ex, _log); - } - } - E(p21, challenge, p24); - return p24; - } - - /// <summary>Creates the LMv2 response for the supplied information.</summary> - /// <remarks>Creates the LMv2 response for the supplied information.</remarks> - /// <param name="domain">The domain in which the username exists.</param> - /// <param name="user">The username.</param> - /// <param name="password">The user's password.</param> - /// <param name="challenge">The server challenge.</param> - /// <param name="clientChallenge">The client challenge (nonce).</param> - public static byte[] GetLMv2Response(string domain, string user, string password, - byte[] challenge, byte[] clientChallenge) - { - try - { - byte[] hash = new byte[16]; - byte[] response = new byte[24]; - // The next 2-1/2 lines of this should be placed with nTOWFv1 in place of password - Md4 md4 = new Md4(); - md4.Update(Runtime.GetBytesForString(password, SmbConstants.UniEncoding) - ); - Hmact64 hmac = new Hmact64(md4.Digest()); - hmac.Update(Runtime.GetBytesForString(user.ToUpper(), SmbConstants.UniEncoding - )); - hmac.Update(Runtime.GetBytesForString(domain.ToUpper(), SmbConstants.UniEncoding - )); - hmac = new Hmact64(hmac.Digest()); - hmac.Update(challenge); - hmac.Update(clientChallenge); - hmac.Digest(response, 0, 16); - Array.Copy(clientChallenge, 0, response, 16, 8); - return response; - } - catch (Exception ex) - { - if (_log.Level > 0) - { - Runtime.PrintStackTrace(ex, _log); - } - return null; - } - } - - public static byte[] GetNtlm2Response(byte[] nTowFv1, byte[] serverChallenge, byte - [] clientChallenge) - { - byte[] sessionHash = new byte[8]; - try - { - MessageDigest md5; - md5 = MessageDigest.GetInstance("MD5"); - md5.Update(serverChallenge); - md5.Update(clientChallenge, 0, 8); - Array.Copy(md5.Digest(), 0, sessionHash, 0, 8); - } - catch (Exception gse) - { - if (_log.Level > 0) - { - Runtime.PrintStackTrace(gse, _log); - } - throw new RuntimeException("MD5", gse); - } - byte[] key = new byte[21]; - Array.Copy(nTowFv1, 0, key, 0, 16); - byte[] ntResponse = new byte[24]; - E(key, sessionHash, ntResponse); - return ntResponse; - } - - public static byte[] NtowFv1(string password) - { - if (password == null) - { - throw new RuntimeException("Password parameter is required"); - } - try - { - Md4 md4 = new Md4(); - md4.Update(Runtime.GetBytesForString(password, SmbConstants.UniEncoding) - ); - return md4.Digest(); - } - catch (UnsupportedEncodingException uee) - { - throw new RuntimeException(uee.Message); - } - } - - public static byte[] NtowFv2(string domain, string username, string password) - { - try - { - Md4 md4 = new Md4(); - md4.Update(Runtime.GetBytesForString(password, SmbConstants.UniEncoding) - ); - Hmact64 hmac = new Hmact64(md4.Digest()); - hmac.Update(Runtime.GetBytesForString(username.ToUpper(), SmbConstants.UniEncoding - )); - hmac.Update(Runtime.GetBytesForString(domain, SmbConstants.UniEncoding)); - return hmac.Digest(); - } - catch (UnsupportedEncodingException uee) - { - throw new RuntimeException(uee.Message); - } - } - - internal static byte[] ComputeResponse(byte[] responseKey, byte[] serverChallenge - , byte[] clientData, int offset, int length) - { - Hmact64 hmac = new Hmact64(responseKey); - hmac.Update(serverChallenge); - hmac.Update(clientData, offset, length); - byte[] mac = hmac.Digest(); - byte[] ret = new byte[mac.Length + clientData.Length]; - Array.Copy(mac, 0, ret, 0, mac.Length); - Array.Copy(clientData, 0, ret, mac.Length, clientData.Length); - return ret; - } - - public static byte[] GetLMv2Response(byte[] responseKeyLm, byte[] serverChallenge - , byte[] clientChallenge) - { - return ComputeResponse(responseKeyLm, serverChallenge - , clientChallenge, 0, clientChallenge.Length); - } - - public static byte[] GetNtlMv2Response(byte[] responseKeyNt, byte[] serverChallenge - , byte[] clientChallenge, long nanos1601, byte[] targetInfo) - { - int targetInfoLength = targetInfo != null ? targetInfo.Length : 0; - byte[] temp = new byte[28 + targetInfoLength + 4]; - Encdec.Enc_uint32le(unchecked(0x00000101), temp, 0); - // Header - Encdec.Enc_uint32le(unchecked(0x00000000), temp, 4); - // Reserved - Encdec.Enc_uint64le(nanos1601, temp, 8); - Array.Copy(clientChallenge, 0, temp, 16, 8); - Encdec.Enc_uint32le(unchecked(0x00000000), temp, 24); - // Unknown - if (targetInfo != null) - { - Array.Copy(targetInfo, 0, temp, 28, targetInfoLength); - } - Encdec.Enc_uint32le(unchecked(0x00000000), temp, 28 + targetInfoLength); - // mystery bytes! - return ComputeResponse(responseKeyNt, serverChallenge - , temp, 0, temp.Length); - } - - internal static readonly NtlmPasswordAuthentication Null = new NtlmPasswordAuthentication - (string.Empty, string.Empty, string.Empty); - - internal static readonly NtlmPasswordAuthentication Guest = new NtlmPasswordAuthentication - ("?", "GUEST", string.Empty); - - internal static readonly NtlmPasswordAuthentication Default = new NtlmPasswordAuthentication - (null); - - internal string Domain; - - internal string Username; - - internal string Password; - - internal byte[] AnsiHash; - - internal byte[] UnicodeHash; - - internal bool HashesExternal; - - internal byte[] ClientChallenge; - - internal byte[] Challenge; - - /// <summary> - /// Create an <tt>NtlmPasswordAuthentication</tt> object from the userinfo - /// component of an SMB URL like "<tt>domain;user:pass</tt>". - /// </summary> - /// <remarks> - /// Create an <tt>NtlmPasswordAuthentication</tt> object from the userinfo - /// component of an SMB URL like "<tt>domain;user:pass</tt>". This constructor - /// is used internally be jCIFS when parsing SMB URLs. - /// </remarks> - public NtlmPasswordAuthentication(string userInfo) - { - Domain = Username = Password = null; - if (userInfo != null) - { - try - { - userInfo = Unescape(userInfo); - } - catch (UnsupportedEncodingException) - { - } - int i; - int u; - int end; - char c; - end = userInfo.Length; - for (i = 0, u = 0; i < end; i++) - { - c = userInfo[i]; - if (c == ';') - { - Domain = Runtime.Substring(userInfo, 0, i); - u = i + 1; - } - else - { - if (c == ':') - { - Password = Runtime.Substring(userInfo, i + 1); - break; - } - } - } - Username = Runtime.Substring(userInfo, u, i); - } - InitDefaults(); - if (Domain == null) - { - Domain = DefaultDomain; - } - if (Username == null) - { - Username = DefaultUsername; - } - if (Password == null) - { - Password = DefaultPassword; - } - } - - /// <summary> - /// Create an <tt>NtlmPasswordAuthentication</tt> object from a - /// domain, username, and password. - /// </summary> - /// <remarks> - /// Create an <tt>NtlmPasswordAuthentication</tt> object from a - /// domain, username, and password. Parameters that are <tt>null</tt> - /// will be substituted with <tt>jcifs.smb.client.domain</tt>, - /// <tt>jcifs.smb.client.username</tt>, <tt>jcifs.smb.client.password</tt> - /// property values. - /// </remarks> - public NtlmPasswordAuthentication(string domain, string username, string password - ) - { - int ci; - if (username != null) - { - ci = username.IndexOf('@'); - if (ci > 0) - { - domain = Runtime.Substring(username, ci + 1); - username = Runtime.Substring(username, 0, ci); - } - else - { - ci = username.IndexOf('\\'); - if (ci > 0) - { - domain = Runtime.Substring(username, 0, ci); - username = Runtime.Substring(username, ci + 1); - } - } - } - this.Domain = domain; - this.Username = username; - this.Password = password; - InitDefaults(); - if (domain == null) - { - this.Domain = DefaultDomain; - } - if (username == null) - { - this.Username = DefaultUsername; - } - if (password == null) - { - this.Password = DefaultPassword; - } - } - - /// <summary> - /// Create an <tt>NtlmPasswordAuthentication</tt> object with raw password - /// hashes. - /// </summary> - /// <remarks> - /// Create an <tt>NtlmPasswordAuthentication</tt> object with raw password - /// hashes. This is used exclusively by the <tt>jcifs.http.NtlmSsp</tt> - /// class which is in turn used by NTLM HTTP authentication functionality. - /// </remarks> - public NtlmPasswordAuthentication(string domain, string username, byte[] challenge - , byte[] ansiHash, byte[] unicodeHash) - { - if (domain == null || username == null || ansiHash == null || unicodeHash == null) - { - throw new ArgumentException("External credentials cannot be null"); - } - this.Domain = domain; - this.Username = username; - Password = null; - this.Challenge = challenge; - this.AnsiHash = ansiHash; - this.UnicodeHash = unicodeHash; - HashesExternal = true; - } - - /// <summary>Returns the domain.</summary> - /// <remarks>Returns the domain.</remarks> - public string GetDomain() - { - return Domain; - } - - /// <summary>Returns the username.</summary> - /// <remarks>Returns the username.</remarks> - public string GetUsername() - { - return Username; - } - - /// <summary> - /// Returns the password in plain text or <tt>null</tt> if the raw password - /// hashes were used to construct this <tt>NtlmPasswordAuthentication</tt> - /// object which will be the case when NTLM HTTP Authentication is - /// used. - /// </summary> - /// <remarks> - /// Returns the password in plain text or <tt>null</tt> if the raw password - /// hashes were used to construct this <tt>NtlmPasswordAuthentication</tt> - /// object which will be the case when NTLM HTTP Authentication is - /// used. There is no way to retrieve a users password in plain text unless - /// it is supplied by the user at runtime. - /// </remarks> - public string GetPassword() - { - return Password; - } - - /// <summary> - /// Return the domain and username in the format: - /// <tt>domain\\username</tt>. - /// </summary> - /// <remarks> - /// Return the domain and username in the format: - /// <tt>domain\\username</tt>. This is equivalent to <tt>toString()</tt>. - /// </remarks> - public new string GetName() - { - bool d = Domain.Length > 0 && Domain.Equals("?") == false; - return d ? Domain + "\\" + Username : Username; - } - - /// <summary>Computes the 24 byte ANSI password hash given the 8 byte server challenge. - /// </summary> - /// <remarks>Computes the 24 byte ANSI password hash given the 8 byte server challenge. - /// </remarks> - public byte[] GetAnsiHash(byte[] challenge) - { - if (HashesExternal) - { - return AnsiHash; - } - switch (LmCompatibility) - { - case 0: - case 1: - { - return GetPreNtlmResponse(Password, challenge); - } - - case 2: - { - return GetNtlmResponse(Password, challenge); - } - - case 3: - case 4: - case 5: - { - if (ClientChallenge == null) - { - ClientChallenge = new byte[8]; - Random.NextBytes(ClientChallenge); - } - return GetLMv2Response(Domain, Username, Password, challenge, ClientChallenge); - } - - default: - { - return GetPreNtlmResponse(Password, challenge); - } - } - } - - /// <summary>Computes the 24 byte Unicode password hash given the 8 byte server challenge. - /// </summary> - /// <remarks>Computes the 24 byte Unicode password hash given the 8 byte server challenge. - /// </remarks> - public byte[] GetUnicodeHash(byte[] challenge) - { - if (HashesExternal) - { - return UnicodeHash; - } - switch (LmCompatibility) - { - case 0: - case 1: - case 2: - { - return GetNtlmResponse(Password, challenge); - } - - case 3: - case 4: - case 5: - { - return new byte[0]; - } - - default: - { - return GetNtlmResponse(Password, challenge); - } - } - } - - /// <exception cref="SharpCifs.Smb.SmbException"></exception> - public byte[] GetSigningKey(byte[] challenge) - { - switch (LmCompatibility) - { - case 0: - case 1: - case 2: - { - byte[] signingKey = new byte[40]; - GetUserSessionKey(challenge, signingKey, 0); - Array.Copy(GetUnicodeHash(challenge), 0, signingKey, 16, 24); - return signingKey; - } - - case 3: - case 4: - case 5: - { - throw new SmbException("NTLMv2 requires extended security (jcifs.smb.client.useExtendedSecurity must be true if jcifs.smb.lmCompatibility >= 3)" - ); - } - } - return null; - } - - /// <summary>Returns the effective user session key.</summary> - /// <remarks>Returns the effective user session key.</remarks> - /// <param name="challenge">The server challenge.</param> - /// <returns> - /// A <code>byte[]</code> containing the effective user session key, - /// used in SMB MAC signing and NTLMSSP signing and sealing. - /// </returns> - public byte[] GetUserSessionKey(byte[] challenge) - { - if (HashesExternal) - { - return null; - } - byte[] key = new byte[16]; - try - { - GetUserSessionKey(challenge, key, 0); - } - catch (Exception ex) - { - if (_log.Level > 0) - { - Runtime.PrintStackTrace(ex, _log); - } - } - return key; - } - - /// <summary>Calculates the effective user session key.</summary> - /// <remarks>Calculates the effective user session key.</remarks> - /// <param name="challenge">The server challenge.</param> - /// <param name="dest"> - /// The destination array in which the user session key will be - /// placed. - /// </param> - /// <param name="offset"> - /// The offset in the destination array at which the - /// session key will start. - /// </param> - /// <exception cref="SharpCifs.Smb.SmbException"></exception> - internal void GetUserSessionKey(byte[] challenge, byte[] dest, int offset) - { - if (HashesExternal) - { - return; - } - try - { - Md4 md4 = new Md4(); - md4.Update(Runtime.GetBytesForString(Password, SmbConstants.UniEncoding) - ); - switch (LmCompatibility) - { - case 0: - case 1: - case 2: - { - md4.Update(md4.Digest()); - md4.Digest(dest, offset, 16); - break; - } - - case 3: - case 4: - case 5: - { - if (ClientChallenge == null) - { - ClientChallenge = new byte[8]; - Random.NextBytes(ClientChallenge); - } - Hmact64 hmac = new Hmact64(md4.Digest()); - hmac.Update(Runtime.GetBytesForString(Username.ToUpper(), SmbConstants.UniEncoding - )); - hmac.Update(Runtime.GetBytesForString(Domain.ToUpper(), SmbConstants.UniEncoding - )); - byte[] ntlmv2Hash = hmac.Digest(); - hmac = new Hmact64(ntlmv2Hash); - hmac.Update(challenge); - hmac.Update(ClientChallenge); - Hmact64 userKey = new Hmact64(ntlmv2Hash); - userKey.Update(hmac.Digest()); - userKey.Digest(dest, offset, 16); - break; - } - - default: - { - md4.Update(md4.Digest()); - md4.Digest(dest, offset, 16); - break; - } - } - } - catch (Exception e) - { - throw new SmbException(string.Empty, e); - } - } - - /// <summary> - /// Compares two <tt>NtlmPasswordAuthentication</tt> objects for - /// equality. - /// </summary> - /// <remarks> - /// Compares two <tt>NtlmPasswordAuthentication</tt> objects for - /// equality. Two <tt>NtlmPasswordAuthentication</tt> objects are equal if - /// their caseless domain and username fields are equal and either both hashes are external and they are equal or both internally supplied passwords are equal. If one <tt>NtlmPasswordAuthentication</tt> object has external hashes (meaning negotiated via NTLM HTTP Authentication) and the other does not they will not be equal. This is technically not correct however the server 8 byte challage would be required to compute and compare the password hashes but that it not available with this method. - /// </remarks> - public override bool Equals(object obj) - { - if (obj is NtlmPasswordAuthentication) - { - NtlmPasswordAuthentication ntlm = (NtlmPasswordAuthentication - )obj; - if (ntlm.Domain.ToUpper().Equals(Domain.ToUpper()) && ntlm.Username.ToUpper().Equals - (Username.ToUpper())) - { - if (HashesExternal && ntlm.HashesExternal) - { - - return Arrays.Equals(AnsiHash, ntlm.AnsiHash) && Arrays.Equals(UnicodeHash, ntlm. - UnicodeHash); - } - if (!HashesExternal && Password.Equals(ntlm.Password)) - { - return true; - } - } - } - return false; - } - - /// <summary>Return the upcased username hash code.</summary> - /// <remarks>Return the upcased username hash code.</remarks> - public override int GetHashCode() - { - return GetName().ToUpper().GetHashCode(); - } - - /// <summary> - /// Return the domain and username in the format: - /// <tt>domain\\username</tt>. - /// </summary> - /// <remarks> - /// Return the domain and username in the format: - /// <tt>domain\\username</tt>. This is equivalent to <tt>getName()</tt>. - /// </remarks> - public override string ToString() - { - return GetName(); - } - - /// <exception cref="System.FormatException"></exception> - /// <exception cref="UnsupportedEncodingException"></exception> - internal static string Unescape(string str) - { - char ch; - int i; - int j; - int state; - int len; - char[] @out; - byte[] b = new byte[1]; - if (str == null) - { - return null; - } - len = str.Length; - @out = new char[len]; - state = 0; - for (i = j = 0; i < len; i++) - { - switch (state) - { - case 0: - { - ch = str[i]; - if (ch == '%') - { - state = 1; - } - else - { - @out[j++] = ch; - } - break; - } - - case 1: - { - b[0] = unchecked((byte)(Convert.ToInt32(Runtime.Substring(str, i, - i + 2), 16) & unchecked(0xFF))); - @out[j++] = (Runtime.GetStringForBytes(b, 0, 1, "ASCII"))[0]; - i++; - state = 0; - break; - } - } - } - return new string(@out, 0, j); - } - } + /// <summary>This class stores and encrypts NTLM user credentials.</summary> + /// <remarks> + /// This class stores and encrypts NTLM user credentials. The default + /// credentials are retrieved from the <tt>jcifs.smb.client.domain</tt>, + /// <tt>jcifs.smb.client.username</tt>, and <tt>jcifs.smb.client.password</tt> + /// properties. + /// <p> + /// Read <a href="../../../authhandler.html">jCIFS Exceptions and + /// NtlmAuthenticator</a> for related information. + /// </remarks> + + public sealed class NtlmPasswordAuthentication : Principal + { + private static readonly int LmCompatibility + = Config.GetInt("jcifs.smb.lmCompatibility", 3); + + private static readonly Random Random = new Random(); + + private static LogStream _log = LogStream.GetInstance(); + + private static readonly byte[] S8 = + { + unchecked(unchecked(0x4b)), + unchecked(unchecked(0x47)), + unchecked(unchecked(0x53)), + unchecked(unchecked(0x21)), + unchecked(unchecked(0x40)), + unchecked(unchecked(0x23)), + unchecked(unchecked(0x24)), + unchecked(unchecked(0x25)) + }; + + // KGS!@#$% + private static void E(byte[] key, byte[] data, byte[] e) + { + byte[] key7 = new byte[7]; + byte[] e8 = new byte[8]; + for (int i = 0; i < key.Length / 7; i++) + { + Array.Copy(key, i * 7, key7, 0, 7); + DES des = new DES(key7); + des.Encrypt(data, e8); + Array.Copy(e8, 0, e, i * 8, 8); + } + } + + internal static string DefaultDomain; + + internal static string DefaultUsername; + + internal static string DefaultPassword; + + internal static readonly string Blank = string.Empty; + + public static readonly NtlmPasswordAuthentication Anonymous + = new NtlmPasswordAuthentication(string.Empty, string.Empty, string.Empty); + + internal static void InitDefaults() + { + if (DefaultDomain != null) + { + return; + } + DefaultDomain = Config.GetProperty("jcifs.smb.client.domain", "?"); + DefaultUsername = Config.GetProperty("jcifs.smb.client.username", "GUEST"); + DefaultPassword = Config.GetProperty("jcifs.smb.client.password", Blank); + } + + /// <summary> + /// Generate the ANSI DES hash for the password associated with these credentials. + /// </summary> + /// <remarks> + /// Generate the ANSI DES hash for the password associated with these credentials. + /// </remarks> + public static byte[] GetPreNtlmResponse(string password, byte[] challenge) + { + byte[] p14 = new byte[14]; + byte[] p21 = new byte[21]; + byte[] p24 = new byte[24]; + byte[] passwordBytes; + try + { + passwordBytes = Runtime.GetBytesForString(password.ToUpper(), SmbConstants.OemEncoding); + } + catch (UnsupportedEncodingException uee) + { + throw new RuntimeException("Try setting jcifs.encoding=US-ASCII", uee); + } + int passwordLength = passwordBytes.Length; + // Only encrypt the first 14 bytes of the password for Pre 0.12 NT LM + if (passwordLength > 14) + { + passwordLength = 14; + } + Array.Copy(passwordBytes, 0, p14, 0, passwordLength); + E(p14, S8, p21); + E(p21, challenge, p24); + return p24; + } + + /// <summary> + /// Generate the Unicode MD4 hash for the password associated with these credentials. + /// </summary> + /// <remarks> + /// Generate the Unicode MD4 hash for the password associated with these credentials. + /// </remarks> + public static byte[] GetNtlmResponse(string password, byte[] challenge) + { + byte[] uni = null; + byte[] p21 = new byte[21]; + byte[] p24 = new byte[24]; + try + { + uni = Runtime.GetBytesForString(password, SmbConstants.UniEncoding); + } + catch (UnsupportedEncodingException uee) + { + if (_log.Level > 0) + { + Runtime.PrintStackTrace(uee, _log); + } + } + Md4 md4 = new Md4(); + md4.Update(uni); + try + { + md4.Digest(p21, 0, 16); + } + catch (Exception ex) + { + if (_log.Level > 0) + { + Runtime.PrintStackTrace(ex, _log); + } + } + E(p21, challenge, p24); + return p24; + } + + /// <summary>Creates the LMv2 response for the supplied information.</summary> + /// <remarks>Creates the LMv2 response for the supplied information.</remarks> + /// <param name="domain">The domain in which the username exists.</param> + /// <param name="user">The username.</param> + /// <param name="password">The user's password.</param> + /// <param name="challenge">The server challenge.</param> + /// <param name="clientChallenge">The client challenge (nonce).</param> + public static byte[] GetLMv2Response(string domain, + string user, + string password, + byte[] challenge, + byte[] clientChallenge) + { + try + { + byte[] hash = new byte[16]; + byte[] response = new byte[24]; + // The next 2-1/2 lines of this should be placed with nTOWFv1 in place of password + Md4 md4 = new Md4(); + md4.Update(Runtime.GetBytesForString(password, SmbConstants.UniEncoding)); + Hmact64 hmac = new Hmact64(md4.Digest()); + hmac.Update(Runtime.GetBytesForString(user.ToUpper(), SmbConstants.UniEncoding)); + hmac.Update(Runtime.GetBytesForString(domain.ToUpper(), SmbConstants.UniEncoding)); + hmac = new Hmact64(hmac.Digest()); + hmac.Update(challenge); + hmac.Update(clientChallenge); + hmac.Digest(response, 0, 16); + Array.Copy(clientChallenge, 0, response, 16, 8); + return response; + } + catch (Exception ex) + { + if (_log.Level > 0) + { + Runtime.PrintStackTrace(ex, _log); + } + return null; + } + } + + public static byte[] GetNtlm2Response(byte[] nTowFv1, + byte[] serverChallenge, + byte[] clientChallenge) + { + byte[] sessionHash = new byte[8]; + try + { + MessageDigest md5; + md5 = MessageDigest.GetInstance("MD5"); + md5.Update(serverChallenge); + md5.Update(clientChallenge, 0, 8); + Array.Copy(md5.Digest(), 0, sessionHash, 0, 8); + } + catch (Exception gse) + { + if (_log.Level > 0) + { + Runtime.PrintStackTrace(gse, _log); + } + throw new RuntimeException("MD5", gse); + } + byte[] key = new byte[21]; + Array.Copy(nTowFv1, 0, key, 0, 16); + byte[] ntResponse = new byte[24]; + E(key, sessionHash, ntResponse); + return ntResponse; + } + + public static byte[] NtowFv1(string password) + { + if (password == null) + { + throw new RuntimeException("Password parameter is required"); + } + try + { + Md4 md4 = new Md4(); + md4.Update(Runtime.GetBytesForString(password, SmbConstants.UniEncoding)); + return md4.Digest(); + } + catch (UnsupportedEncodingException uee) + { + throw new RuntimeException(uee.Message); + } + } + + public static byte[] NtowFv2(string domain, string username, string password) + { + try + { + Md4 md4 = new Md4(); + md4.Update(Runtime.GetBytesForString(password, SmbConstants.UniEncoding)); + Hmact64 hmac = new Hmact64(md4.Digest()); + hmac.Update(Runtime.GetBytesForString(username.ToUpper(), SmbConstants.UniEncoding)); + hmac.Update(Runtime.GetBytesForString(domain, SmbConstants.UniEncoding)); + return hmac.Digest(); + } + catch (UnsupportedEncodingException uee) + { + throw new RuntimeException(uee.Message); + } + } + + internal static byte[] ComputeResponse(byte[] responseKey, + byte[] serverChallenge, + byte[] clientData, + int offset, + int length) + { + Hmact64 hmac = new Hmact64(responseKey); + hmac.Update(serverChallenge); + hmac.Update(clientData, offset, length); + byte[] mac = hmac.Digest(); + byte[] ret = new byte[mac.Length + clientData.Length]; + Array.Copy(mac, 0, ret, 0, mac.Length); + Array.Copy(clientData, 0, ret, mac.Length, clientData.Length); + return ret; + } + + public static byte[] GetLMv2Response(byte[] responseKeyLm, + byte[] serverChallenge, + byte[] clientChallenge) + { + return ComputeResponse(responseKeyLm, + serverChallenge, + clientChallenge, + 0, + clientChallenge.Length); + } + + public static byte[] GetNtlMv2Response(byte[] responseKeyNt, + byte[] serverChallenge, + byte[] clientChallenge, + long nanos1601, + byte[] targetInfo) + { + int targetInfoLength = targetInfo != null + ? targetInfo.Length + : 0; + byte[] temp = new byte[28 + targetInfoLength + 4]; + Encdec.Enc_uint32le(unchecked(0x00000101), temp, 0); + // Header + Encdec.Enc_uint32le(unchecked(0x00000000), temp, 4); + // Reserved + Encdec.Enc_uint64le(nanos1601, temp, 8); + Array.Copy(clientChallenge, 0, temp, 16, 8); + Encdec.Enc_uint32le(unchecked(0x00000000), temp, 24); + // Unknown + if (targetInfo != null) + { + Array.Copy(targetInfo, 0, temp, 28, targetInfoLength); + } + Encdec.Enc_uint32le(unchecked(0x00000000), temp, 28 + targetInfoLength); + // mystery bytes! + return ComputeResponse(responseKeyNt, + serverChallenge, + temp, + 0, + temp.Length); + } + + internal static readonly NtlmPasswordAuthentication Null + = new NtlmPasswordAuthentication(string.Empty, string.Empty, string.Empty); + + internal static readonly NtlmPasswordAuthentication Guest + = new NtlmPasswordAuthentication("?", "GUEST", string.Empty); + + internal static readonly NtlmPasswordAuthentication Default + = new NtlmPasswordAuthentication(null); + + internal string Domain; + + internal string Username; + + internal string Password; + + internal byte[] AnsiHash; + + internal byte[] UnicodeHash; + + internal bool HashesExternal; + + internal byte[] ClientChallenge; + + internal byte[] Challenge; + + /// <summary> + /// Create an <tt>NtlmPasswordAuthentication</tt> object from the userinfo + /// component of an SMB URL like "<tt>domain;user:pass</tt>". + /// </summary> + /// <remarks> + /// Create an <tt>NtlmPasswordAuthentication</tt> object from the userinfo + /// component of an SMB URL like "<tt>domain;user:pass</tt>". This constructor + /// is used internally be jCIFS when parsing SMB URLs. + /// </remarks> + public NtlmPasswordAuthentication(string userInfo) + { + Domain = Username = Password = null; + if (userInfo != null) + { + try + { + userInfo = Unescape(userInfo); + } + catch (UnsupportedEncodingException) + { + } + int i; + int u; + int end; + char c; + end = userInfo.Length; + for (i = 0, u = 0; i < end; i++) + { + c = userInfo[i]; + if (c == ';') + { + Domain = Runtime.Substring(userInfo, 0, i); + u = i + 1; + } + else + { + if (c == ':') + { + Password = Runtime.Substring(userInfo, i + 1); + break; + } + } + } + Username = Runtime.Substring(userInfo, u, i); + } + InitDefaults(); + if (Domain == null) + { + Domain = DefaultDomain; + } + if (Username == null) + { + Username = DefaultUsername; + } + if (Password == null) + { + Password = DefaultPassword; + } + } + + /// <summary> + /// Create an <tt>NtlmPasswordAuthentication</tt> object from a + /// domain, username, and password. + /// </summary> + /// <remarks> + /// Create an <tt>NtlmPasswordAuthentication</tt> object from a + /// domain, username, and password. Parameters that are <tt>null</tt> + /// will be substituted with <tt>jcifs.smb.client.domain</tt>, + /// <tt>jcifs.smb.client.username</tt>, <tt>jcifs.smb.client.password</tt> + /// property values. + /// </remarks> + public NtlmPasswordAuthentication(string domain, string username, string password) + { + int ci; + if (username != null) + { + ci = username.IndexOf('@'); + if (ci > 0) + { + domain = Runtime.Substring(username, ci + 1); + username = Runtime.Substring(username, 0, ci); + } + else + { + ci = username.IndexOf('\\'); + if (ci > 0) + { + domain = Runtime.Substring(username, 0, ci); + username = Runtime.Substring(username, ci + 1); + } + } + } + this.Domain = domain; + this.Username = username; + this.Password = password; + InitDefaults(); + if (domain == null) + { + this.Domain = DefaultDomain; + } + if (username == null) + { + this.Username = DefaultUsername; + } + if (password == null) + { + this.Password = DefaultPassword; + } + } + + /// <summary> + /// Create an <tt>NtlmPasswordAuthentication</tt> object with raw password + /// hashes. + /// </summary> + /// <remarks> + /// Create an <tt>NtlmPasswordAuthentication</tt> object with raw password + /// hashes. This is used exclusively by the <tt>jcifs.http.NtlmSsp</tt> + /// class which is in turn used by NTLM HTTP authentication functionality. + /// </remarks> + public NtlmPasswordAuthentication(string domain, + string username, + byte[] challenge, + byte[] ansiHash, + byte[] unicodeHash) + { + if (domain == null + || username == null + || ansiHash == null + || unicodeHash == null) + { + throw new ArgumentException("External credentials cannot be null"); + } + this.Domain = domain; + this.Username = username; + Password = null; + this.Challenge = challenge; + this.AnsiHash = ansiHash; + this.UnicodeHash = unicodeHash; + HashesExternal = true; + } + + /// <summary>Returns the domain.</summary> + /// <remarks>Returns the domain.</remarks> + public string GetDomain() + { + return Domain; + } + + /// <summary>Returns the username.</summary> + /// <remarks>Returns the username.</remarks> + public string GetUsername() + { + return Username; + } + + /// <summary> + /// Returns the password in plain text or <tt>null</tt> if the raw password + /// hashes were used to construct this <tt>NtlmPasswordAuthentication</tt> + /// object which will be the case when NTLM HTTP Authentication is + /// used. + /// </summary> + /// <remarks> + /// Returns the password in plain text or <tt>null</tt> if the raw password + /// hashes were used to construct this <tt>NtlmPasswordAuthentication</tt> + /// object which will be the case when NTLM HTTP Authentication is + /// used. There is no way to retrieve a users password in plain text unless + /// it is supplied by the user at runtime. + /// </remarks> + public string GetPassword() + { + return Password; + } + + /// <summary> + /// Return the domain and username in the format: + /// <tt>domain\\username</tt>. + /// </summary> + /// <remarks> + /// Return the domain and username in the format: + /// <tt>domain\\username</tt>. This is equivalent to <tt>toString()</tt>. + /// </remarks> + public new string GetName() + { + bool d = Domain.Length > 0 && Domain.Equals("?") == false; + return d + ? Domain + "\\" + Username + : Username; + } + + /// <summary> + /// Computes the 24 byte ANSI password hash given the 8 byte server challenge. + /// </summary> + /// <remarks> + /// Computes the 24 byte ANSI password hash given the 8 byte server challenge. + /// </remarks> + public byte[] GetAnsiHash(byte[] challenge) + { + if (HashesExternal) + { + return AnsiHash; + } + switch (LmCompatibility) + { + case 0: + case 1: + { + return GetPreNtlmResponse(Password, challenge); + } + + case 2: + { + return GetNtlmResponse(Password, challenge); + } + + case 3: + case 4: + case 5: + { + if (ClientChallenge == null) + { + ClientChallenge = new byte[8]; + Random.NextBytes(ClientChallenge); + } + return GetLMv2Response(Domain, + Username, + Password, + challenge, + ClientChallenge); + } + + default: + { + return GetPreNtlmResponse(Password, challenge); + } + } + } + + /// <summary> + /// Computes the 24 byte Unicode password hash given the 8 byte server challenge. + /// </summary> + /// <remarks> + /// Computes the 24 byte Unicode password hash given the 8 byte server challenge. + /// </remarks> + public byte[] GetUnicodeHash(byte[] challenge) + { + if (HashesExternal) + { + return UnicodeHash; + } + switch (LmCompatibility) + { + case 0: + case 1: + case 2: + { + return GetNtlmResponse(Password, challenge); + } + + case 3: + case 4: + case 5: + { + return new byte[0]; + } + + default: + { + return GetNtlmResponse(Password, challenge); + } + } + } + + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + public byte[] GetSigningKey(byte[] challenge) + { + switch (LmCompatibility) + { + case 0: + case 1: + case 2: + { + byte[] signingKey = new byte[40]; + GetUserSessionKey(challenge, signingKey, 0); + Array.Copy(GetUnicodeHash(challenge), 0, signingKey, 16, 24); + return signingKey; + } + + case 3: + case 4: + case 5: + { + throw new SmbException( + "NTLMv2 requires extended security " + + "(jcifs.smb.client.useExtendedSecurity must be true " + + "if jcifs.smb.lmCompatibility >= 3)"); + } + } + return null; + } + + /// <summary>Returns the effective user session key.</summary> + /// <remarks>Returns the effective user session key.</remarks> + /// <param name="challenge">The server challenge.</param> + /// <returns> + /// A <code>byte[]</code> containing the effective user session key, + /// used in SMB MAC signing and NTLMSSP signing and sealing. + /// </returns> + public byte[] GetUserSessionKey(byte[] challenge) + { + if (HashesExternal) + { + return null; + } + byte[] key = new byte[16]; + try + { + GetUserSessionKey(challenge, key, 0); + } + catch (Exception ex) + { + if (_log.Level > 0) + { + Runtime.PrintStackTrace(ex, _log); + } + } + return key; + } + + /// <summary>Calculates the effective user session key.</summary> + /// <remarks>Calculates the effective user session key.</remarks> + /// <param name="challenge">The server challenge.</param> + /// <param name="dest"> + /// The destination array in which the user session key will be + /// placed. + /// </param> + /// <param name="offset"> + /// The offset in the destination array at which the + /// session key will start. + /// </param> + /// <exception cref="SharpCifs.Smb.SmbException"></exception> + internal void GetUserSessionKey(byte[] challenge, byte[] dest, int offset) + { + if (HashesExternal) + { + return; + } + try + { + Md4 md4 = new Md4(); + md4.Update(Runtime.GetBytesForString(Password, SmbConstants.UniEncoding)); + switch (LmCompatibility) + { + case 0: + case 1: + case 2: + { + md4.Update(md4.Digest()); + md4.Digest(dest, offset, 16); + break; + } + + case 3: + case 4: + case 5: + { + if (ClientChallenge == null) + { + ClientChallenge = new byte[8]; + Random.NextBytes(ClientChallenge); + } + Hmact64 hmac = new Hmact64(md4.Digest()); + hmac.Update(Runtime.GetBytesForString(Username.ToUpper(), + SmbConstants.UniEncoding)); + hmac.Update(Runtime.GetBytesForString(Domain.ToUpper(), + SmbConstants.UniEncoding)); + byte[] ntlmv2Hash = hmac.Digest(); + hmac = new Hmact64(ntlmv2Hash); + hmac.Update(challenge); + hmac.Update(ClientChallenge); + Hmact64 userKey = new Hmact64(ntlmv2Hash); + userKey.Update(hmac.Digest()); + userKey.Digest(dest, offset, 16); + break; + } + + default: + { + md4.Update(md4.Digest()); + md4.Digest(dest, offset, 16); + break; + } + } + } + catch (Exception e) + { + throw new SmbException(string.Empty, e); + } + } + + /// <summary> + /// Compares two <tt>NtlmPasswordAuthentication</tt> objects for + /// equality. + /// </summary> + /// <remarks> + /// Compares two <tt>NtlmPasswordAuthentication</tt> objects for + /// equality. Two <tt>NtlmPasswordAuthentication</tt> objects are equal if + /// their caseless domain and username fields are equal and either both hashes are external and they are equal or both internally supplied passwords are equal. If one <tt>NtlmPasswordAuthentication</tt> object has external hashes (meaning negotiated via NTLM HTTP Authentication) and the other does not they will not be equal. This is technically not correct however the server 8 byte challage would be required to compute and compare the password hashes but that it not available with this method. + /// </remarks> + public override bool Equals(object obj) + { + if (obj is NtlmPasswordAuthentication) + { + NtlmPasswordAuthentication ntlm = (NtlmPasswordAuthentication)obj; + if (ntlm.Domain.ToUpper().Equals(Domain.ToUpper()) + && ntlm.Username.ToUpper().Equals(Username.ToUpper())) + { + if (HashesExternal && ntlm.HashesExternal) + { + + return Arrays.Equals(AnsiHash, ntlm.AnsiHash) + && Arrays.Equals(UnicodeHash, ntlm.UnicodeHash); + } + if (!HashesExternal && Password.Equals(ntlm.Password)) + { + return true; + } + } + } + return false; + } + + /// <summary>Return the upcased username hash code.</summary> + /// <remarks>Return the upcased username hash code.</remarks> + public override int GetHashCode() + { + return GetName().ToUpper().GetHashCode(); + } + + /// <summary> + /// Return the domain and username in the format: + /// <tt>domain\\username</tt>. + /// </summary> + /// <remarks> + /// Return the domain and username in the format: + /// <tt>domain\\username</tt>. This is equivalent to <tt>getName()</tt>. + /// </remarks> + public override string ToString() + { + return GetName(); + } + + /// <exception cref="System.FormatException"></exception> + /// <exception cref="UnsupportedEncodingException"></exception> + internal static string Unescape(string str) + { + char ch; + int i; + int j; + int state; + int len; + char[] @out; + byte[] b = new byte[1]; + if (str == null) + { + return null; + } + len = str.Length; + @out = new char[len]; + state = 0; + for (i = j = 0; i < len; i++) + { + switch (state) + { + case 0: + { + ch = str[i]; + if (ch == '%') + { + state = 1; + } + else + { + @out[j++] = ch; + } + break; + } + + case 1: + { + b[0] = unchecked( + (byte)( + Convert.ToInt32(Runtime.Substring(str, i, i + 2), 16) + & unchecked(0xFF) + ) + ); + @out[j++] = (Runtime.GetStringForBytes(b, 0, 1, "ASCII"))[0]; + i++; + state = 0; + break; + } + } + } + return new string(@out, 0, j); + } + } } |
