RemoteServiceHelper.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. /* Copyright (C) 2014-2018 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.Text;
  10. using SMBLibrary.RPC;
  11. using Utilities;
  12. namespace SMBLibrary.Services
  13. {
  14. public class RemoteServiceHelper
  15. {
  16. // v1 - DCE 1.1: Remote Procedure Call
  17. // v2 - [MS-RPCE] 2.2.4.12 NDR Transfer Syntax Identifier
  18. public static readonly Guid NDRTransferSyntaxIdentifier = new Guid("8A885D04-1CEB-11C9-9FE8-08002B104860");
  19. public const int NDRTransferSyntaxVersion = 2;
  20. // v1 - [MS-RPCE] 3.3.1.5.3 - Bind Time Feature Negotiation
  21. // Windows will reject this:
  22. //private static readonly Guid BindTimeFeatureIdentifier1 = new Guid("6CB71C2C-9812-4540-0100-000000000000");
  23. // Windows will return NegotiationResult.NegotiateAck:
  24. public static readonly Guid BindTimeFeatureIdentifier3 = new Guid("6CB71C2C-9812-4540-0300-000000000000");
  25. public const int BindTimeFeatureIdentifierVersion = 1;
  26. private static uint m_associationGroupID = 1;
  27. public static BindAckPDU GetRPCBindResponse(BindPDU bindPDU, RemoteService service)
  28. {
  29. BindAckPDU bindAckPDU = new BindAckPDU();
  30. bindAckPDU.Flags = PacketFlags.FirstFragment | PacketFlags.LastFragment;
  31. bindAckPDU.DataRepresentation = bindPDU.DataRepresentation;
  32. bindAckPDU.CallID = bindPDU.CallID;
  33. // See DCE 1.1: Remote Procedure Call - 12.6.3.6
  34. // The client should set the assoc_group_id field either to 0 (zero), to indicate a new association group,
  35. // or to the known value. When the server receives a value of 0, this indicates that the client
  36. // has requested a new association group, and it assigns a server unique value to the group.
  37. if (bindPDU.AssociationGroupID == 0)
  38. {
  39. bindAckPDU.AssociationGroupID = m_associationGroupID;
  40. m_associationGroupID++;
  41. if (m_associationGroupID == 0)
  42. {
  43. m_associationGroupID++;
  44. }
  45. }
  46. else
  47. {
  48. bindAckPDU.AssociationGroupID = bindPDU.AssociationGroupID;
  49. }
  50. bindAckPDU.SecondaryAddress = @"\PIPE\" + service.PipeName;
  51. bindAckPDU.MaxTransmitFragmentSize = bindPDU.MaxReceiveFragmentSize;
  52. bindAckPDU.MaxReceiveFragmentSize = bindPDU.MaxTransmitFragmentSize;
  53. foreach (ContextElement element in bindPDU.ContextList)
  54. {
  55. ResultElement resultElement = new ResultElement();
  56. if (element.AbstractSyntax.InterfaceUUID.Equals(service.InterfaceGuid))
  57. {
  58. int index = IndexOfSupportedTransferSyntax(element.TransferSyntaxList);
  59. if (index >= 0)
  60. {
  61. resultElement.Result = NegotiationResult.Acceptance;
  62. resultElement.TransferSyntax = element.TransferSyntaxList[index];
  63. }
  64. else if (element.TransferSyntaxList.Contains(new SyntaxID(BindTimeFeatureIdentifier3, 1)))
  65. {
  66. // [MS-RPCE] 3.3.1.5.3
  67. // If the server supports bind time feature negotiation, it MUST reply with the result
  68. // field in the p_result_t structure of the bind_ack PDU equal to negotiate_ack.
  69. resultElement.Result = NegotiationResult.NegotiateAck;
  70. resultElement.Reason = RejectionReason.AbstractSyntaxNotSupported;
  71. }
  72. else
  73. {
  74. resultElement.Result = NegotiationResult.ProviderRejection;
  75. resultElement.Reason = RejectionReason.ProposedTransferSyntaxesNotSupported;
  76. }
  77. }
  78. else
  79. {
  80. resultElement.Result = NegotiationResult.ProviderRejection;
  81. resultElement.Reason = RejectionReason.AbstractSyntaxNotSupported;
  82. }
  83. bindAckPDU.ResultList.Add(resultElement);
  84. }
  85. return bindAckPDU;
  86. }
  87. private static int IndexOfSupportedTransferSyntax(List<SyntaxID> syntaxList)
  88. {
  89. List<SyntaxID> supportedTransferSyntaxes = new List<SyntaxID>();
  90. supportedTransferSyntaxes.Add(new SyntaxID(NDRTransferSyntaxIdentifier, 1));
  91. // [MS-RPCE] Version 2.0 data representation protocol:
  92. supportedTransferSyntaxes.Add(new SyntaxID(NDRTransferSyntaxIdentifier, 2));
  93. for(int index = 0; index < syntaxList.Count; index++)
  94. {
  95. if (supportedTransferSyntaxes.Contains(syntaxList[index]))
  96. {
  97. return index;
  98. }
  99. }
  100. return -1;
  101. }
  102. public static List<RPCPDU> GetRPCResponse(RequestPDU requestPDU, RemoteService service, int maxTransmitFragmentSize)
  103. {
  104. List<RPCPDU> result = new List<RPCPDU>();
  105. byte[] responseBytes;
  106. try
  107. {
  108. responseBytes = service.GetResponseBytes(requestPDU.OpNum, requestPDU.Data);
  109. }
  110. catch (UnsupportedOpNumException)
  111. {
  112. FaultPDU faultPDU = new FaultPDU();
  113. faultPDU.Flags = PacketFlags.FirstFragment | PacketFlags.LastFragment | PacketFlags.DidNotExecute;
  114. faultPDU.DataRepresentation = requestPDU.DataRepresentation;
  115. faultPDU.CallID = requestPDU.CallID;
  116. faultPDU.AllocationHint = RPCPDU.CommonFieldsLength + FaultPDU.FaultFieldsLength;
  117. // Windows will return either nca_s_fault_ndr or nca_op_rng_error.
  118. faultPDU.Status = FaultStatus.OpRangeError;
  119. result.Add(faultPDU);
  120. return result;
  121. }
  122. int offset = 0;
  123. int maxPDUDataLength = maxTransmitFragmentSize - RPCPDU.CommonFieldsLength - ResponsePDU.ResponseFieldsLength;
  124. do
  125. {
  126. ResponsePDU responsePDU = new ResponsePDU();
  127. int pduDataLength = Math.Min(responseBytes.Length - offset, maxPDUDataLength);
  128. responsePDU.DataRepresentation = requestPDU.DataRepresentation;
  129. responsePDU.CallID = requestPDU.CallID;
  130. responsePDU.AllocationHint = (uint)(responseBytes.Length - offset);
  131. responsePDU.Data = ByteReader.ReadBytes(responseBytes, offset, pduDataLength);
  132. if (offset == 0)
  133. {
  134. responsePDU.Flags |= PacketFlags.FirstFragment;
  135. }
  136. if (offset + pduDataLength == responseBytes.Length)
  137. {
  138. responsePDU.Flags |= PacketFlags.LastFragment;
  139. }
  140. result.Add(responsePDU);
  141. offset += pduDataLength;
  142. }
  143. while (offset < responseBytes.Length);
  144. return result;
  145. }
  146. }
  147. }