NTTransactHelper.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218
  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.IO;
  10. using SMBLibrary.SMB1;
  11. using Utilities;
  12. namespace SMBLibrary.Server.SMB1
  13. {
  14. internal class NTTransactHelper
  15. {
  16. /// <summary>
  17. /// The client MUST send as many secondary requests as are needed to complete the transfer of the transaction request.
  18. /// </summary>
  19. internal static List<SMB1Command> GetNTTransactResponse(SMB1Header header, NTTransactRequest request, ISMBShare share, SMB1ConnectionState state)
  20. {
  21. if (request.TransParameters.Length < request.TotalParameterCount ||
  22. request.TransData.Length < request.TotalDataCount)
  23. {
  24. // A secondary transaction request is pending
  25. ProcessStateObject processState = state.CreateProcessState(header.PID);
  26. processState.SubcommandID = (ushort)request.Function;
  27. processState.MaxParameterCount = request.MaxParameterCount;
  28. processState.MaxDataCount = request.MaxDataCount;
  29. processState.TransactionSetup = request.Setup;
  30. processState.TransactionParameters = new byte[request.TotalParameterCount];
  31. processState.TransactionData = new byte[request.TotalDataCount];
  32. ByteWriter.WriteBytes(processState.TransactionParameters, 0, request.TransParameters);
  33. ByteWriter.WriteBytes(processState.TransactionData, 0, request.TransData);
  34. processState.TransactionParametersReceived += request.TransParameters.Length;
  35. processState.TransactionDataReceived += request.TransData.Length;
  36. return new NTTransactInterimResponse();
  37. }
  38. else
  39. {
  40. // We have a complete command
  41. return GetCompleteNTTransactResponse(header, request.MaxParameterCount, request.MaxDataCount, request.Function, request.Setup, request.TransParameters, request.TransData, share, state);
  42. }
  43. }
  44. /// <summary>
  45. /// There are no secondary response messages.
  46. /// The client MUST send as many secondary requests as are needed to complete the transfer of the transaction request.
  47. /// </summary>
  48. internal static List<SMB1Command> GetNTTransactResponse(SMB1Header header, NTTransactSecondaryRequest request, ISMBShare share, SMB1ConnectionState state)
  49. {
  50. ProcessStateObject processState = state.GetProcessState(header.PID);
  51. if (processState == null)
  52. {
  53. throw new InvalidDataException();
  54. }
  55. ByteWriter.WriteBytes(processState.TransactionParameters, (int)request.ParameterDisplacement, request.TransParameters);
  56. ByteWriter.WriteBytes(processState.TransactionData, (int)request.DataDisplacement, request.TransData);
  57. processState.TransactionParametersReceived += request.TransParameters.Length;
  58. processState.TransactionDataReceived += request.TransData.Length;
  59. if (processState.TransactionParametersReceived < processState.TransactionParameters.Length ||
  60. processState.TransactionDataReceived < processState.TransactionData.Length)
  61. {
  62. return new List<SMB1Command>();
  63. }
  64. else
  65. {
  66. // We have a complete command
  67. state.RemoveProcessState(header.PID);
  68. return GetCompleteNTTransactResponse(header, processState.MaxParameterCount, processState.MaxDataCount, (NTTransactSubcommandName)processState.SubcommandID, processState.TransactionSetup, processState.TransactionParameters, processState.TransactionData, share, state);
  69. }
  70. }
  71. internal static List<SMB1Command> GetCompleteNTTransactResponse(SMB1Header header, uint maxParameterCount, uint maxDataCount, NTTransactSubcommandName subcommandName, byte[] requestSetup, byte[] requestParameters, byte[] requestData, ISMBShare share, SMB1ConnectionState state)
  72. {
  73. NTTransactSubcommand subcommand;
  74. try
  75. {
  76. subcommand = NTTransactSubcommand.GetSubcommandRequest(subcommandName, requestSetup, requestParameters, requestData, header.UnicodeFlag);
  77. }
  78. catch
  79. {
  80. // [MS-CIFS] If the Function code is not defined, the server MUST return STATUS_INVALID_SMB.
  81. header.Status = NTStatus.STATUS_INVALID_SMB;
  82. return new ErrorResponse(CommandName.SMB_COM_NT_TRANSACT);
  83. }
  84. state.LogToServer(Severity.Verbose, "Received complete SMB_COM_NT_TRANSACT subcommand: {0}", subcommand.SubcommandName);
  85. NTTransactSubcommand subcommandResponse = null;
  86. if (subcommand is NTTransactCreateRequest)
  87. {
  88. header.Status = NTStatus.STATUS_NOT_IMPLEMENTED;
  89. }
  90. else if (subcommand is NTTransactIOCTLRequest)
  91. {
  92. subcommandResponse = GetSubcommandResponse(header, maxDataCount, (NTTransactIOCTLRequest)subcommand, share, state);
  93. }
  94. else if (subcommand is NTTransactSetSecurityDescriptor)
  95. {
  96. header.Status = NTStatus.STATUS_NOT_IMPLEMENTED;
  97. }
  98. else if (subcommand is NTTransactNotifyChangeRequest)
  99. {
  100. NotifyChangeHelper.ProcessNTTransactNotifyChangeRequest(header, maxParameterCount, (NTTransactNotifyChangeRequest)subcommand, share, state);
  101. if (header.Status == NTStatus.STATUS_PENDING)
  102. {
  103. return new List<SMB1Command>();
  104. }
  105. }
  106. else if (subcommand is NTTransactQuerySecurityDescriptorRequest)
  107. {
  108. header.Status = NTStatus.STATUS_NOT_IMPLEMENTED;
  109. }
  110. else
  111. {
  112. // [MS-CIFS] If the Function code is defined but not implemented, the server MUST return STATUS_SMB_BAD_COMMAND.
  113. header.Status = NTStatus.STATUS_SMB_BAD_COMMAND;
  114. }
  115. if (subcommandResponse == null)
  116. {
  117. return new ErrorResponse(CommandName.SMB_COM_NT_TRANSACT);
  118. }
  119. byte[] responseSetup = subcommandResponse.GetSetup();
  120. byte[] responseParameters = subcommandResponse.GetParameters(header.UnicodeFlag);
  121. byte[] responseData = subcommandResponse.GetData();
  122. return GetNTTransactResponse(responseSetup, responseParameters, responseData, state.MaxBufferSize);
  123. }
  124. private static NTTransactIOCTLResponse GetSubcommandResponse(SMB1Header header, uint maxDataCount, NTTransactIOCTLRequest subcommand, ISMBShare share, SMB1ConnectionState state)
  125. {
  126. SMB1Session session = state.GetSession(header.UID);
  127. string ctlCode = Enum.IsDefined(typeof(IoControlCode), subcommand.FunctionCode) ? ((IoControlCode)subcommand.FunctionCode).ToString() : ("0x" + subcommand.FunctionCode.ToString("x"));
  128. if (!subcommand.IsFsctl)
  129. {
  130. // [MS-SMB] If the IsFsctl field is set to zero, the server SHOULD fail the request with STATUS_NOT_SUPPORTED
  131. state.LogToServer(Severity.Verbose, "IOCTL: Non-FSCTL requests are not supported. CTL Code: {0}", ctlCode);
  132. header.Status = NTStatus.STATUS_NOT_SUPPORTED;
  133. return null;
  134. }
  135. OpenFileObject openFile = session.GetOpenFileObject(subcommand.FID);
  136. if (openFile == null)
  137. {
  138. state.LogToServer(Severity.Verbose, "IOCTL failed. CTL Code: {0}. Invalid FID. (UID: {1}, TID: {2}, FID: {3})", ctlCode, header.UID, header.TID, subcommand.FID);
  139. header.Status = NTStatus.STATUS_INVALID_HANDLE;
  140. return null;
  141. }
  142. int maxOutputLength = (int)maxDataCount;
  143. byte[] output;
  144. header.Status = share.FileStore.DeviceIOControl(openFile.Handle, subcommand.FunctionCode, subcommand.Data, out output, maxOutputLength);
  145. if (header.Status != NTStatus.STATUS_SUCCESS && header.Status != NTStatus.STATUS_BUFFER_OVERFLOW)
  146. {
  147. state.LogToServer(Severity.Verbose, "IOCTL failed. CTL Code: {0}. NTStatus: {1}.", ctlCode, header.Status);
  148. return null;
  149. }
  150. state.LogToServer(Severity.Verbose, "IOCTL succeeded. CTL Code: {0}.", ctlCode);
  151. NTTransactIOCTLResponse response = new NTTransactIOCTLResponse();
  152. response.Data = output;
  153. return response;
  154. }
  155. internal static List<SMB1Command> GetNTTransactResponse(byte[] responseSetup, byte[] responseParameters, byte[] responseData, int maxBufferSize)
  156. {
  157. List<SMB1Command> result = new List<SMB1Command>();
  158. NTTransactResponse response = new NTTransactResponse();
  159. result.Add(response);
  160. int responseSize = NTTransactResponse.CalculateMessageSize(responseSetup.Length, responseParameters.Length, responseData.Length);
  161. if (responseSize <= maxBufferSize)
  162. {
  163. response.Setup = responseSetup;
  164. response.TotalParameterCount = (ushort)responseParameters.Length;
  165. response.TotalDataCount = (ushort)responseData.Length;
  166. response.TransParameters = responseParameters;
  167. response.TransData = responseData;
  168. }
  169. else
  170. {
  171. int currentDataLength = maxBufferSize - (responseSize - responseData.Length);
  172. byte[] buffer = new byte[currentDataLength];
  173. Array.Copy(responseData, 0, buffer, 0, currentDataLength);
  174. response.Setup = responseSetup;
  175. response.TotalParameterCount = (ushort)responseParameters.Length;
  176. response.TotalDataCount = (ushort)responseData.Length;
  177. response.TransParameters = responseParameters;
  178. response.TransData = buffer;
  179. int dataBytesLeftToSend = responseData.Length - currentDataLength;
  180. while (dataBytesLeftToSend > 0)
  181. {
  182. NTTransactResponse additionalResponse = new NTTransactResponse();
  183. currentDataLength = dataBytesLeftToSend;
  184. responseSize = TransactionResponse.CalculateMessageSize(0, 0, dataBytesLeftToSend);
  185. if (responseSize > maxBufferSize)
  186. {
  187. currentDataLength = maxBufferSize - (responseSize - dataBytesLeftToSend);
  188. }
  189. buffer = new byte[currentDataLength];
  190. int dataBytesSent = responseData.Length - dataBytesLeftToSend;
  191. Array.Copy(responseData, dataBytesSent, buffer, 0, currentDataLength);
  192. additionalResponse.TotalParameterCount = (ushort)responseParameters.Length;
  193. additionalResponse.TotalDataCount = (ushort)responseData.Length;
  194. additionalResponse.TransData = buffer;
  195. additionalResponse.ParameterDisplacement = (ushort)response.TransParameters.Length;
  196. additionalResponse.DataDisplacement = (ushort)dataBytesSent;
  197. result.Add(additionalResponse);
  198. dataBytesLeftToSend -= currentDataLength;
  199. }
  200. }
  201. return result;
  202. }
  203. }
  204. }