GSSProvider.cs 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  1. /* Copyright (C) 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 SMBLibrary.Authentication.NTLM;
  10. using Utilities;
  11. namespace SMBLibrary.Authentication.GSSAPI
  12. {
  13. public class GSSProvider
  14. {
  15. public static readonly byte[] NTLMSSPIdentifier = new byte[] { 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x02, 0x0a };
  16. private List<IGSSMechanism> m_mechanisms;
  17. private Dictionary<object, IGSSMechanism> m_contextToMechanism = new Dictionary<object, IGSSMechanism>();
  18. public GSSProvider(IGSSMechanism mechanism)
  19. {
  20. m_mechanisms = new List<IGSSMechanism>();
  21. m_mechanisms.Add(mechanism);
  22. }
  23. public GSSProvider(List<IGSSMechanism> mechanisms)
  24. {
  25. m_mechanisms = mechanisms;
  26. }
  27. public byte[] GetSPNEGOTokenInitBytes()
  28. {
  29. SimpleProtectedNegotiationTokenInit token = new SimpleProtectedNegotiationTokenInit();
  30. token.MechanismTypeList = new List<byte[]>();
  31. foreach (IGSSMechanism mechanism in m_mechanisms)
  32. {
  33. token.MechanismTypeList.Add(mechanism.Identifier);
  34. }
  35. return SimpleProtectedNegotiationToken.GetTokenBytes(token);
  36. }
  37. public NTStatus AcceptSecurityContext(ref object context, byte[] inputToken, out byte[] outputToken)
  38. {
  39. outputToken = null;
  40. SimpleProtectedNegotiationToken spnegoToken = SimpleProtectedNegotiationToken.ReadToken(inputToken, 0);
  41. if (spnegoToken != null)
  42. {
  43. if (spnegoToken is SimpleProtectedNegotiationTokenInit)
  44. {
  45. SimpleProtectedNegotiationTokenInit tokenInit = (SimpleProtectedNegotiationTokenInit)spnegoToken;
  46. IGSSMechanism mechanism = FindMechanism(tokenInit.MechanismTypeList);
  47. if (mechanism != null)
  48. {
  49. byte[] mechanismOutput;
  50. NTStatus status = mechanism.AcceptSecurityContext(ref context, tokenInit.MechanismToken, out mechanismOutput);
  51. outputToken = GetSPNEGOTokenResponseBytes(mechanismOutput, status, mechanism.Identifier);
  52. m_contextToMechanism[context] = mechanism;
  53. return status;
  54. }
  55. return NTStatus.SEC_E_SECPKG_NOT_FOUND;
  56. }
  57. else // SimpleProtectedNegotiationTokenResponse
  58. {
  59. IGSSMechanism mechanism;
  60. if (!m_contextToMechanism.TryGetValue(context, out mechanism))
  61. {
  62. // We assume that the problem is not with our implementation and that the client has sent
  63. // SimpleProtectedNegotiationTokenResponse without first sending SimpleProtectedNegotiationTokenInit.
  64. return NTStatus.SEC_E_INVALID_TOKEN;
  65. }
  66. SimpleProtectedNegotiationTokenResponse tokenResponse = (SimpleProtectedNegotiationTokenResponse)spnegoToken;
  67. byte[] mechanismOutput;
  68. NTStatus status = mechanism.AcceptSecurityContext(ref context, tokenResponse.ResponseToken, out mechanismOutput);
  69. outputToken = GetSPNEGOTokenResponseBytes(mechanismOutput, status, null);
  70. return status;
  71. }
  72. }
  73. else
  74. {
  75. // [MS-SMB] The Windows GSS implementation supports raw Kerberos / NTLM messages in the SecurityBlob.
  76. // [MS-SMB2] Windows [..] will also accept raw Kerberos messages and implicit NTLM messages as part of GSS authentication.
  77. if (AuthenticationMessageUtils.IsSignatureValid(inputToken))
  78. {
  79. MessageTypeName messageType = AuthenticationMessageUtils.GetMessageType(inputToken);
  80. IGSSMechanism ntlmAuthenticationProvider = FindMechanism(NTLMSSPIdentifier);
  81. if (ntlmAuthenticationProvider != null)
  82. {
  83. NTStatus status = ntlmAuthenticationProvider.AcceptSecurityContext(ref context, inputToken, out outputToken);
  84. if (messageType == MessageTypeName.Negotiate)
  85. {
  86. m_contextToMechanism[context] = ntlmAuthenticationProvider;
  87. }
  88. return status;
  89. }
  90. else
  91. {
  92. return NTStatus.SEC_E_SECPKG_NOT_FOUND;
  93. }
  94. }
  95. }
  96. return NTStatus.SEC_E_INVALID_TOKEN;
  97. }
  98. public object GetContextAttribute(object context, GSSAttributeName attributeName)
  99. {
  100. IGSSMechanism mechanism;
  101. if (!m_contextToMechanism.TryGetValue(context, out mechanism))
  102. {
  103. return null;
  104. }
  105. return mechanism.GetContextAttribute(context, attributeName);
  106. }
  107. public bool DeleteSecurityContext(ref object context)
  108. {
  109. bool result = false;
  110. if (context != null)
  111. {
  112. IGSSMechanism mechanism;
  113. if (m_contextToMechanism.TryGetValue(context, out mechanism))
  114. {
  115. object contextReference = context;
  116. result = mechanism.DeleteSecurityContext(ref context);
  117. if (result)
  118. {
  119. m_contextToMechanism.Remove(contextReference);
  120. }
  121. }
  122. }
  123. return result;
  124. }
  125. /// <summary>
  126. /// Helper method for legacy implementation.
  127. /// </summary>
  128. public NTStatus GetNTLMChallengeMessage(out object context, NegotiateMessage negotiateMessage, out ChallengeMessage challengeMessage)
  129. {
  130. context = null;
  131. challengeMessage = null;
  132. IGSSMechanism ntlmAuthenticationProvider = FindMechanism(NTLMSSPIdentifier);
  133. if (ntlmAuthenticationProvider != null)
  134. {
  135. byte[] outputToken;
  136. NTStatus result = ntlmAuthenticationProvider.AcceptSecurityContext(ref context, negotiateMessage.GetBytes(), out outputToken);
  137. challengeMessage = new ChallengeMessage(outputToken);
  138. m_contextToMechanism.Add(context, ntlmAuthenticationProvider);
  139. return result;
  140. }
  141. else
  142. {
  143. return NTStatus.SEC_E_SECPKG_NOT_FOUND;
  144. }
  145. }
  146. /// <summary>
  147. /// Helper method for legacy implementation.
  148. /// </summary>
  149. public NTStatus NTLMAuthenticate(object context, AuthenticateMessage authenticateMessage)
  150. {
  151. IGSSMechanism ntlmAuthenticationProvider = FindMechanism(NTLMSSPIdentifier);
  152. if (ntlmAuthenticationProvider != null)
  153. {
  154. byte[] outputToken;
  155. NTStatus result = ntlmAuthenticationProvider.AcceptSecurityContext(ref context, authenticateMessage.GetBytes(), out outputToken);
  156. return result;
  157. }
  158. else
  159. {
  160. return NTStatus.SEC_E_SECPKG_NOT_FOUND;
  161. }
  162. }
  163. public IGSSMechanism FindMechanism(List<byte[]> mechanismIdentifiers)
  164. {
  165. foreach (byte[] identifier in mechanismIdentifiers)
  166. {
  167. IGSSMechanism mechanism = FindMechanism(identifier);
  168. if (mechanism != null)
  169. {
  170. return mechanism;
  171. }
  172. }
  173. return null;
  174. }
  175. public IGSSMechanism FindMechanism(byte[] mechanismIdentifier)
  176. {
  177. foreach (IGSSMechanism mechanism in m_mechanisms)
  178. {
  179. if (ByteUtils.AreByteArraysEqual(mechanism.Identifier, mechanismIdentifier))
  180. {
  181. return mechanism;
  182. }
  183. }
  184. return null;
  185. }
  186. private static byte[] GetSPNEGOTokenResponseBytes(byte[] mechanismOutput, NTStatus status, byte[] mechanismIdentifier)
  187. {
  188. SimpleProtectedNegotiationTokenResponse tokenResponse = new SimpleProtectedNegotiationTokenResponse();
  189. if (status == NTStatus.STATUS_SUCCESS)
  190. {
  191. tokenResponse.NegState = NegState.AcceptCompleted;
  192. }
  193. else if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
  194. {
  195. tokenResponse.NegState = NegState.AcceptIncomplete;
  196. }
  197. else
  198. {
  199. tokenResponse.NegState = NegState.Reject;
  200. }
  201. tokenResponse.SupportedMechanism = mechanismIdentifier;
  202. tokenResponse.ResponseToken = mechanismOutput;
  203. return tokenResponse.GetBytes();
  204. }
  205. }
  206. }