/// <summary> /// Constructor. /// </summary> /// <param name="max_recv_fragment">The initial maximum receive fragment length.</param> /// <param name="max_send_fragment">The initial maximum send fragment length.</param> /// <param name="transport_security">The transport security for the connection.</param> /// <param name="data_rep">The data representation.</param> protected RpcConnectedClientTransport(ushort max_recv_fragment, ushort max_send_fragment, NdrDataRepresentation data_rep, RpcTransportSecurity transport_security) { _max_recv_fragment = max_recv_fragment; _max_send_fragment = max_send_fragment; _data_rep = data_rep; _security_context = new Dictionary <int, RpcTransportSecurityContext>(); _current_security_context = new RpcTransportSecurityContext(this, transport_security, _current_context_id++); _security_context[_current_security_context.ContextId] = _current_security_context; switch (transport_security.AuthenticationLevel) { case RpcAuthenticationLevel.PacketIntegrity: case RpcAuthenticationLevel.PacketPrivacy: _auth_data_required = true; break; } if (DisableBindTimeFeatureNegotiation) { _bind_time_features = BindTimeFeatureNegotiation.None; } }
private byte[] SendReceiveRequestPDU(int proc_num, Guid objuuid, byte[] stub_data, RpcTransportSecurityContext security_context) { try { CallId++; PDURequest request_pdu = new PDURequest() { OpNum = (short)proc_num, ObjectUUID = objuuid }; int max_fragment = _max_send_fragment - request_pdu.HeaderLength; int auth_data_length = 0; if (_auth_data_required) { auth_data_length = security_context.AuthDataLength; max_fragment -= (auth_data_length + AuthData.PDU_AUTH_DATA_HEADER_SIZE); max_fragment &= ~0xF; } List <byte[]> fragments = PDURequest.DoFragment(stub_data, max_fragment); for (int i = 0; i < fragments.Count; ++i) { PDUHeader pdu_header = new PDUHeader() { MajorVersion = PDUHeader.RPC_VERSION_MAJOR, MinorVersion = PDUHeader.RPC_VERSION_MINOR, DataRep = _data_rep, CallId = CallId, Type = PDUType.Request }; if (i == 0) { pdu_header.Flags |= PDUFlags.FirstFrag; } if (i == fragments.Count - 1) { pdu_header.Flags |= PDUFlags.LastFrag; } byte[] stub_fragment = fragments[i]; byte[] auth_data = new byte[0]; byte[] header = request_pdu.ToArray(pdu_header, stub_fragment.Length, 0); if (_auth_data_required) { int auth_data_padding = 0; int auth_trailing_size = (header.Length + stub_fragment.Length + AuthData.PDU_AUTH_DATA_HEADER_SIZE) & 0xF; if (auth_trailing_size != 0) { auth_data_padding = 16 - auth_trailing_size; Array.Resize(ref stub_fragment, stub_fragment.Length + auth_data_padding); } header = request_pdu.ToArray(pdu_header, stub_fragment.Length + AuthData.PDU_AUTH_DATA_HEADER_SIZE, auth_data_length); auth_data = security_context.ProtectPDU(header, ref stub_fragment, auth_data_padding, _send_sequence_no); } MemoryStream send_stm = new MemoryStream(); BinaryWriter writer = new BinaryWriter(send_stm); writer.Write(header); writer.Write(stub_fragment); writer.Write(auth_data); byte[] fragment = send_stm.ToArray(); string name = fragments.Count == 1 ? $"{GetType().Name} Send Buffer" : $"{GetType().Name} Send Buffer - Fragment {i}"; RpcUtils.DumpBuffer(true, name, fragment); if (!WriteFragment(fragment)) { throw new RpcTransportException("Failed to write out PDU buffer."); } _send_sequence_no++; } MemoryStream recv_stm = new MemoryStream(); PDUHeader curr_header = new PDUHeader(); int frag_count = 0; while ((curr_header.Flags & PDUFlags.LastFrag) == 0) { var pdu = ReadPDU(frag_count++); curr_header = pdu.Item1; AuthData auth_data = pdu.Item3; if (curr_header.CallId != CallId) { throw new RpcTransportException($"Mismatching call ID - {curr_header.CallId} should be {CallId}."); } if (auth_data.ContextId != security_context.ContextId) { security_context = GetContext(auth_data.ContextId); } var recv_pdu = CheckFault(curr_header.ToPDU(pdu.Item2)); if (recv_pdu is PDUResponse resp_pdu) { byte[] resp_stub_data = _auth_data_required ? security_context.UnprotectPDU(resp_pdu.ToArray(curr_header), resp_pdu.StubData, auth_data, _recv_sequence_no) : resp_pdu.StubData; _recv_sequence_no++; recv_stm.Write(resp_stub_data, 0, resp_stub_data.Length); } else { throw new RpcTransportException($"Unexpected {recv_pdu.PDUType} PDU from server."); } } return(recv_stm.ToArray()); } catch (EndOfStreamException) { throw new RpcTransportException("End of stream."); } }
private void BindAuth(bool alter_context, RpcTransportSecurityContext security_context) { // 8 should be more than enough legs to complete authentication. int max_legs = security_context.MaxAuthLegs; int call_id = ++CallId; int count = 0; while (count++ < max_legs) { PDUBind bind_pdu = new PDUBind(_max_send_fragment, _max_recv_fragment, 0, alter_context); bind_pdu.Elements.Add(new ContextElement(_interface_id, _interface_version, _transfer_syntax_id, _transfer_syntax_version)); if (!_bind_time_features.HasValue) { _bind_time_features = BindTimeFeatureNegotiation.None; bind_pdu.Elements.Add(new ContextElement(_interface_id, _interface_version, BindTimeFeatureNegotiation.SecurityContextMultiplexingSupported)); } var recv = SendReceivePDU(call_id, bind_pdu, security_context.AuthContext.Token.ToArray(), true, security_context); if (recv.Item1 is PDUBindAck bind_ack) { if (bind_ack.ResultList.Count < 1 || bind_ack.ResultList[0].Result != PresentationResultType.Acceptance) { throw new RpcTransportException($"Bind to {_interface_id}:{_interface_version} was rejected."); } if (bind_ack.ResultList.Count == 2) { _bind_time_features = bind_ack.ResultList[1].BindTimeFeature; } if (!alter_context) { // Only capture values from the BindAck. _max_recv_fragment = bind_ack.MaxRecvFrag; _max_send_fragment = bind_ack.MaxXmitFrag; _assoc_group_id = bind_ack.AssocGroupId; alter_context = true; } if (recv.Item2.Data == null || recv.Item2.Data.Length == 0) { // No auth, assume success. break; } security_context.AuthContext.Continue(new AuthenticationToken(recv.Item2.Data)); if (security_context.AuthContext.Done) { byte[] token = security_context.AuthContext.Token.ToArray(); if (token.Length == 0) { break; } // If we still have an NTLM token to complete then send as an Auth3 PDU. if (security_context.TransportSecurity.AuthenticationType == RpcAuthenticationType.WinNT) { SendReceivePDU(call_id, new PDUAuth3(), token, false, security_context); break; } } } else if (recv.Item1 is PDUBindNack bind_nack) { throw new RpcTransportException($"Bind NACK returned with rejection reason {bind_nack.RejectionReason}"); } else { throw new RpcTransportException($"Unexpected {recv.Item1.PDUType} PDU from server."); } } if (!security_context.AuthContext.Done) { throw new RpcTransportException("Failed to complete the client authentication."); } security_context.SetNegotiatedAuthType(); }
private Tuple <PDUBase, AuthData> SendReceivePDU(int call_id, PDUBase send_pdu, byte[] auth_data, bool receive_pdu, RpcTransportSecurityContext security_context) { try { int trailing_auth_length = auth_data.Length > 0 ? auth_data.Length + AuthData.PDU_AUTH_DATA_HEADER_SIZE : 0; PDUHeader pdu_header = new PDUHeader() { MajorVersion = PDUHeader.RPC_VERSION_MAJOR, MinorVersion = PDUHeader.RPC_VERSION_MINOR, DataRep = _data_rep, CallId = CallId, Type = send_pdu.PDUType, Flags = PDUFlags.LastFrag | PDUFlags.FirstFrag, AuthLength = checked ((ushort)auth_data.Length) }; byte[] pdu_data = send_pdu.ToArray(); int pdu_data_length = pdu_data.Length + PDUHeader.PDU_HEADER_SIZE; int auth_padding = 0; if (auth_data.Length > 0 && (pdu_data_length & 15) != 0 && send_pdu.PDUType != PDUType.Auth3) { auth_padding = 16 - (pdu_data_length & 15); } pdu_header.FragmentLength = checked ((ushort)(pdu_data.Length + PDUHeader.PDU_HEADER_SIZE + trailing_auth_length + auth_padding)); MemoryStream send_stm = new MemoryStream(); BinaryWriter writer = new BinaryWriter(send_stm); pdu_header.Write(writer); writer.Write(pdu_data); if (auth_data.Length > 0) { writer.Write(new byte[auth_padding]); new AuthData(security_context.TransportSecurity.AuthenticationType, security_context.TransportSecurity.AuthenticationLevel, auth_padding, security_context.ContextId, auth_data).Write(writer, auth_padding); } byte[] fragment = send_stm.ToArray(); RpcUtils.DumpBuffer(true, $"{GetType().Name} Send Buffer", fragment); if (!WriteFragment(fragment)) { throw new RpcTransportException("Failed to write out PDU buffer."); } _send_sequence_no++; if (!receive_pdu) { return(null); } var pdu = ReadPDU(0); var curr_header = pdu.Item1; if (!curr_header.Flags.HasFlagAllSet(PDUFlags.LastFrag | PDUFlags.FirstFrag)) { throw new RpcTransportException($"Invalid PDU flags {curr_header.Flags}."); } if (curr_header.CallId != call_id) { throw new RpcTransportException($"Mismatching call ID - {curr_header.CallId} should be {call_id}."); } if (pdu.Item3.ContextId != security_context.ContextId) { throw new RpcTransportException($"Mismatching context ID - {pdu.Item3.ContextId} should be {security_context.ContextId}."); } _recv_sequence_no++; return(Tuple.Create(CheckFault(curr_header.ToPDU(pdu.Item2)), pdu.Item3)); } catch (EndOfStreamException) { throw new RpcTransportException("End of stream."); } }