Win32UserCollection.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. /* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
  2. *
  3. * You can redistribute this program and/or modify it under the terms of
  4. * the GNU Lesser Public License as published by the Free Software Foundation,
  5. * either version 3 of the License, or (at your option) any later version.
  6. */
  7. using System;
  8. using System.Collections.Generic;
  9. using System.Net;
  10. using System.Text;
  11. using Utilities;
  12. using SMBLibrary.Authentication;
  13. using SMBLibrary.Win32.Security;
  14. using Microsoft.Win32;
  15. namespace SMBLibrary.Server.Win32
  16. {
  17. public class Win32UserCollection : UserCollection, INTLMAuthenticationProvider
  18. {
  19. private SecHandle m_serverContext;
  20. private byte[] m_serverChallenge = new byte[8];
  21. public Win32UserCollection()
  22. {
  23. List<string> users = NetworkAPI.EnumerateNetworkUsers();
  24. foreach (string user in users)
  25. {
  26. this.Add(new User(user, String.Empty));
  27. }
  28. }
  29. public ChallengeMessage GetChallengeMessage(NegotiateMessage negotiateMessage)
  30. {
  31. byte[] negotiateMessageBytes = negotiateMessage.GetBytes();
  32. byte[] challengeMessageBytes = SSPIHelper.GetType2Message(negotiateMessageBytes, out m_serverContext);
  33. ChallengeMessage challengeMessage = new ChallengeMessage(challengeMessageBytes);
  34. m_serverChallenge = challengeMessage.ServerChallenge;
  35. return challengeMessage;
  36. }
  37. /// <summary>
  38. /// Authenticate will return false when the password is correct in these cases:
  39. /// 1. The correct password is blank and 'limitblankpassworduse' is set to 1.
  40. /// 2. The user is listed in the "Deny access to this computer from the network" list.
  41. /// </summary>
  42. public bool Authenticate(AuthenticateMessage message)
  43. {
  44. if ((message.NegotiateFlags & NegotiateFlags.Anonymous) > 0)
  45. {
  46. return this.EnableGuestLogin;
  47. }
  48. // AuthenticateType3Message is not reliable when 'limitblankpassworduse' is set to 1 and the user has an empty password set.
  49. // Note: Windows LogonUser API calls will be listed in the security event log.
  50. if (!AreEmptyPasswordsAllowed() &&
  51. IsPasswordEmpty(message) &&
  52. LoginAPI.HasEmptyPassword(message.UserName))
  53. {
  54. if (FallbackToGuest(message.UserName))
  55. {
  56. return false;
  57. }
  58. else
  59. {
  60. throw new EmptyPasswordNotAllowedException();
  61. }
  62. }
  63. byte[] messageBytes = message.GetBytes();
  64. try
  65. {
  66. return SSPIHelper.AuthenticateType3Message(m_serverContext, messageBytes);
  67. }
  68. catch (Exception)
  69. {
  70. return false;
  71. }
  72. }
  73. public bool IsPasswordEmpty(AuthenticateMessage message)
  74. {
  75. // See [MS-NLMP] 3.3.1 - NTLM v1 Authentication
  76. // Special case for anonymous authentication:
  77. if (message.LmChallengeResponse.Length == 1 || message.NtChallengeResponse.Length == 0)
  78. {
  79. return true;
  80. }
  81. if ((message.NegotiateFlags & NegotiateFlags.ExtendedSecurity) > 0)
  82. {
  83. if (AuthenticationMessageUtils.IsNTLMv1ExtendedSecurity(message.LmChallengeResponse))
  84. {
  85. // NTLM v1 extended security:
  86. byte[] clientChallenge = ByteReader.ReadBytes(message.LmChallengeResponse, 0, 8);
  87. byte[] emptyPasswordNTLMv1Response = NTLMCryptography.ComputeNTLMv1ExtendedSecurityResponse(m_serverChallenge, clientChallenge, String.Empty);
  88. if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv1Response, message.NtChallengeResponse))
  89. {
  90. return true;
  91. }
  92. }
  93. else
  94. {
  95. // NTLM v2:
  96. byte[] _LMv2ClientChallenge = ByteReader.ReadBytes(message.LmChallengeResponse, 16, 8);
  97. byte[] emptyPasswordLMv2Response = NTLMCryptography.ComputeLMv2Response(m_serverChallenge, _LMv2ClientChallenge, String.Empty, message.UserName, message.DomainName);
  98. if (ByteUtils.AreByteArraysEqual(emptyPasswordLMv2Response, message.LmChallengeResponse))
  99. {
  100. return true;
  101. }
  102. if (AuthenticationMessageUtils.IsNTLMv2NTResponse(message.NtChallengeResponse))
  103. {
  104. byte[] clientNTProof = ByteReader.ReadBytes(message.NtChallengeResponse, 0, 16);
  105. byte[] clientChallengeStructurePadded = ByteReader.ReadBytes(message.NtChallengeResponse, 16, message.NtChallengeResponse.Length - 16);
  106. byte[] emptyPasswordNTProof = NTLMCryptography.ComputeNTLMv2Proof(m_serverChallenge, clientChallengeStructurePadded, String.Empty, message.UserName, message.DomainName);
  107. if (ByteUtils.AreByteArraysEqual(clientNTProof, emptyPasswordNTProof))
  108. {
  109. return true;
  110. }
  111. }
  112. }
  113. }
  114. else
  115. {
  116. // NTLM v1:
  117. byte[] emptyPasswordLMv1Response = NTLMCryptography.ComputeLMv1Response(m_serverChallenge, String.Empty);
  118. if (ByteUtils.AreByteArraysEqual(emptyPasswordLMv1Response, message.LmChallengeResponse))
  119. {
  120. return true;
  121. }
  122. byte[] emptyPasswordNTLMv1Response = NTLMCryptography.ComputeNTLMv1Response(m_serverChallenge, String.Empty);
  123. if (ByteUtils.AreByteArraysEqual(emptyPasswordNTLMv1Response, message.NtChallengeResponse))
  124. {
  125. return true;
  126. }
  127. }
  128. return false;
  129. }
  130. public bool FallbackToGuest(string userName)
  131. {
  132. return (EnableGuestLogin && (IndexOf(userName) == -1));
  133. }
  134. /// <summary>
  135. /// We immitate Windows, Guest logins are disabled in any of these cases:
  136. /// 1. The Guest account is disabled.
  137. /// 2. The Guest account has password set.
  138. /// 3. The Guest account is listed in the "deny access to this computer from the network" list.
  139. /// </summary>
  140. private bool EnableGuestLogin
  141. {
  142. get
  143. {
  144. return LoginAPI.ValidateUserPassword("Guest", String.Empty, LogonType.Network);
  145. }
  146. }
  147. public static bool AreEmptyPasswordsAllowed()
  148. {
  149. RegistryKey key = Registry.LocalMachine.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Lsa");
  150. object value = key.GetValue("limitblankpassworduse", 1);
  151. if (value is int)
  152. {
  153. if ((int)value != 0)
  154. {
  155. return false;
  156. }
  157. }
  158. return true;
  159. }
  160. }
  161. }