private static void UpdateSMB2Header(SMB2Command response, SMB2Command request, ConnectionState state) { response.Header.MessageID = request.Header.MessageID; response.Header.CreditCharge = request.Header.CreditCharge; response.Header.Credits = Math.Max((ushort)1, request.Header.Credits); response.Header.IsRelatedOperations = request.Header.IsRelatedOperations; response.Header.Reserved = request.Header.Reserved; if (response.Header.SessionID == 0) { response.Header.SessionID = request.Header.SessionID; } if (response.Header.TreeID == 0) { response.Header.TreeID = request.Header.TreeID; } bool signingRequired = false; if (state is SMB2ConnectionState) { SMB2Session session = ((SMB2ConnectionState)state).GetSession(response.Header.SessionID); if (session != null && session.SigningRequired) { signingRequired = true; } } // [MS-SMB2] The server SHOULD sign the message [..] if the request was signed by the client, // and the response is not an interim response to an asynchronously processed request. bool isInterimResponse = (response.Header.IsAsync && response.Header.Status == NTStatus.STATUS_PENDING); response.Header.IsSigned = (request.Header.IsSigned || signingRequired) && !isInterimResponse; }
internal static void EnqueueResponse(ConnectionState state, SMB2Command response) { List<SMB2Command> responseChain = new List<SMB2Command>(); responseChain.Add(response); EnqueueResponseChain(state, responseChain); }
public SMB1ConnectionState(ConnectionState state) : base(state) { }
private void ProcessPacket(SessionPacket packet, ref ConnectionState state) { if (packet is SessionRequestPacket && m_transport == SMBTransportType.NetBiosOverTCP) { PositiveSessionResponsePacket response = new PositiveSessionResponsePacket(); state.SendQueue.Enqueue(response); } else if (packet is SessionKeepAlivePacket && m_transport == SMBTransportType.NetBiosOverTCP) { // [RFC 1001] NetBIOS session keep alives do not require a response from the NetBIOS peer } else if (packet is SessionMessagePacket) { // Note: To be compatible with SMB2 specifications, we must accept SMB_COM_NEGOTIATE. // We will disconnect the connection if m_enableSMB1 == false and the client does not support SMB2. bool acceptSMB1 = (state.Dialect == SMBDialect.NotSet || state.Dialect == SMBDialect.NTLM012); bool acceptSMB2 = (m_enableSMB2 && (state.Dialect == SMBDialect.NotSet || state.Dialect == SMBDialect.SMB202 || state.Dialect == SMBDialect.SMB210)); if (SMB1Header.IsValidSMB1Header(packet.Trailer)) { if (!acceptSMB1) { state.LogToServer(Severity.Verbose, "Rejected SMB1 message"); state.ClientSocket.Dispose(); return; } SMB1Message message = null; try { message = SMB1Message.GetSMB1Message(packet.Trailer); } catch (Exception ex) { state.LogToServer(Severity.Warning, "Invalid SMB1 message: " + ex.Message); state.ClientSocket.Dispose(); return; } state.LogToServer(Severity.Verbose, "SMB1 message received: {0} requests, First request: {1}, Packet length: {2}", message.Commands.Count, message.Commands[0].CommandName.ToString(), packet.Length); if (state.Dialect == SMBDialect.NotSet && m_enableSMB2) { // Check if the client supports SMB 2 List <string> smb2Dialects = SMB2.NegotiateHelper.FindSMB2Dialects(message); if (smb2Dialects.Count > 0) { SMB2Command response = SMB2.NegotiateHelper.GetNegotiateResponse(smb2Dialects, m_securityProvider, state, m_serverGuid, m_serverStartTime); if (state.Dialect != SMBDialect.NotSet) { state = new SMB2ConnectionState(state); m_connectionManager.AddConnection(state); } EnqueueResponse(state, response); return; } } if (m_enableSMB1) { ProcessSMB1Message(message, ref state); } else { // [MS-SMB2] 3.3.5.3.2 If the string is not present in the dialect list and the server does not implement SMB, // the server MUST disconnect the connection [..] without sending a response. state.LogToServer(Severity.Verbose, "Rejected SMB1 message"); state.ClientSocket.Dispose(); } } else if (SMB2Header.IsValidSMB2Header(packet.Trailer)) { if (!acceptSMB2) { state.LogToServer(Severity.Verbose, "Rejected SMB2 message"); state.ClientSocket.Dispose(); return; } List <SMB2Command> requestChain; try { requestChain = SMB2Command.ReadRequestChain(packet.Trailer, 0); } catch (Exception ex) { state.LogToServer(Severity.Warning, "Invalid SMB2 request chain: " + ex.Message); state.ClientSocket.Dispose(); return; } state.LogToServer(Severity.Verbose, "SMB2 request chain received: {0} requests, First request: {1}, Packet length: {2}", requestChain.Count, requestChain[0].CommandName.ToString(), packet.Length); ProcessSMB2RequestChain(requestChain, ref state); } else { state.LogToServer(Severity.Warning, "Invalid SMB message"); state.ClientSocket.Dispose(); } } else { state.LogToServer(Severity.Warning, "Invalid NetBIOS packet"); state.ClientSocket.Dispose(); return; } }
private void ReceiveCallback(object sender, SocketAsyncEventArgs result) { ConnectionState state = (ConnectionState)result.UserToken; Socket clientSocket = state.ClientSocket; if (!m_listening) { clientSocket.Dispose(); return; } int numberOfBytesReceived; try { numberOfBytesReceived = result.BytesTransferred; } catch (ObjectDisposedException) { state.LogToServer(Severity.Debug, "The connection was terminated"); m_connectionManager.ReleaseConnection(state); return; } catch (SocketException ex) { ///const int WSAECONNRESET = 10054; if (ex.SocketErrorCode == SocketError.ConnectionReset) { state.LogToServer(Severity.Debug, "The connection was forcibly closed by the remote host"); } else { state.LogToServer(Severity.Debug, "The connection was terminated, Socket error code: {0}", ex.SocketErrorCode); } m_connectionManager.ReleaseConnection(state); return; } if (numberOfBytesReceived == 0) { state.LogToServer(Severity.Debug, "The client closed the connection"); m_connectionManager.ReleaseConnection(state); return; } state.UpdateLastReceiveDT(); NBTConnectionReceiveBuffer receiveBuffer = state.ReceiveBuffer; receiveBuffer.SetNumberOfBytesReceived(numberOfBytesReceived); ProcessConnectionBuffer(ref state); if (clientSocket.Connected) { try { SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.SetBuffer(state.ReceiveBuffer.Buffer, state.ReceiveBuffer.WriteOffset, state.ReceiveBuffer.AvailableLength); args.Completed += ReceiveCallback; args.UserToken = state; //m_currentAsyncResult = if (!clientSocket.ReceiveAsync(args)) { ReceiveCallback(sender, args); } } catch (ObjectDisposedException) { m_connectionManager.ReleaseConnection(state); } catch (SocketException) { m_connectionManager.ReleaseConnection(state); } } }
// This method accepts new connections private void ConnectRequestCallback(object sender, SocketAsyncEventArgs ar) { Socket listenerSocket = (Socket)ar.UserToken; Socket clientSocket = ar.AcceptSocket; // Windows will set the TCP keepalive timeout to 120 seconds for an SMB connection SocketUtils.SetKeepAlive(clientSocket, TimeSpan.FromMinutes(2)); // Disable the Nagle Algorithm for this tcp socket: clientSocket.NoDelay = true; IPEndPoint clientEndPoint = (IPEndPoint)clientSocket.RemoteEndPoint; EventHandler <ConnectionRequestEventArgs> handler = ConnectionRequested; bool acceptConnection = true; if (handler != null) { ConnectionRequestEventArgs connectionRequestArgs = new ConnectionRequestEventArgs(clientEndPoint); handler(this, connectionRequestArgs); acceptConnection = connectionRequestArgs.Accept; } if (acceptConnection) { ConnectionState state = new ConnectionState(clientSocket, clientEndPoint, Log); state.LogToServer(Severity.Verbose, "New connection request accepted"); Thread senderThread = new Thread(delegate() { ProcessSendQueue(state); }); senderThread.IsBackground = true; senderThread.Start(); try { // Direct TCP transport packet is actually an NBT Session Message Packet, // So in either case (NetBios over TCP or Direct TCP Transport) we will receive an NBT packet. SocketAsyncEventArgs args = new SocketAsyncEventArgs(); args.SetBuffer(state.ReceiveBuffer.Buffer, state.ReceiveBuffer.WriteOffset, state.ReceiveBuffer.AvailableLength); args.Completed += ReceiveCallback; args.UserToken = state; if (!clientSocket.ReceiveAsync(args)) { ReceiveCallback(clientSocket, args); } } catch (ObjectDisposedException) { } catch (SocketException) { } } else { Log(Severity.Verbose, "[{0}:{1}] New connection request rejected", clientEndPoint.Address, clientEndPoint.Port); clientSocket.Dispose(); } acceptConnection: try { ar.AcceptSocket = null; ar.UserToken = listenerSocket; if (listenerSocket.AcceptAsync(ar) == false) { ConnectRequestCallback(sender, ar); } } catch (ObjectDisposedException) { return; } catch (SocketException ex) { ///const int WSAECONNRESET = 10054; // The client may have closed the connection before we start to process the connection request. ///const int WSAETIMEDOUT = 10060; // The client did not properly respond after a period of time. // When we get WSAECONNRESET or WSAETIMEDOUT, we have to continue to accept other connection requests. // See http://stackoverflow.com/questions/7704417/socket-endaccept-error-10054 if (ex.SocketErrorCode == SocketError.ConnectionReset || ex.SocketErrorCode == SocketError.TimedOut) { goto acceptConnection; } Log(Severity.Debug, "Connection request error {0}", ex.SocketErrorCode); return; } }
private void ReceiveCallback(IAsyncResult result) { ConnectionState state = (ConnectionState)result.AsyncState; Socket clientSocket = state.ClientSocket; if (!m_listening) { clientSocket.Close(); return; } int numberOfBytesReceived; try { numberOfBytesReceived = clientSocket.EndReceive(result); } catch (ObjectDisposedException) { state.LogToServer(Severity.Debug, "The connection was terminated"); m_connectionManager.ReleaseConnection(state); return; } catch (SocketException ex) { const int WSAECONNRESET = 10054; if (ex.ErrorCode == WSAECONNRESET) { state.LogToServer(Severity.Debug, "The connection was forcibly closed by the remote host"); } else { state.LogToServer(Severity.Debug, "The connection was terminated, Socket error code: {0}", ex.ErrorCode); } m_connectionManager.ReleaseConnection(state); return; } if (numberOfBytesReceived == 0) { state.LogToServer(Severity.Debug, "The client closed the connection"); m_connectionManager.ReleaseConnection(state); return; } state.UpdateLastReceiveDT(); NBTConnectionReceiveBuffer receiveBuffer = state.ReceiveBuffer; receiveBuffer.SetNumberOfBytesReceived(numberOfBytesReceived); ProcessConnectionBuffer(ref state); if (clientSocket.Connected) { try { clientSocket.BeginReceive(state.ReceiveBuffer.Buffer, state.ReceiveBuffer.WriteOffset, state.ReceiveBuffer.AvailableLength, 0, ReceiveCallback, state); } catch (ObjectDisposedException) { m_connectionManager.ReleaseConnection(state); } catch (SocketException) { m_connectionManager.ReleaseConnection(state); } } }
/// <summary> /// May return an empty list /// </summary> private List <SMB1Command> ProcessSMB1Command(SMB1Header header, SMB1Command command, ref ConnectionState state) { if (state.Dialect == SMBDialect.NotSet) { if (command is NegotiateRequest) { NegotiateRequest request = (NegotiateRequest)command; if (request.Dialects.Contains(SMBServer.NTLanManagerDialect)) { state = new SMB1ConnectionState(state); state.Dialect = SMBDialect.NTLM012; m_connectionManager.AddConnection(state); if (EnableExtendedSecurity && header.ExtendedSecurityFlag) { return(NegotiateHelper.GetNegotiateResponseExtended(request, m_serverGuid)); } else { return(NegotiateHelper.GetNegotiateResponse(header, request, m_securityProvider, state)); } } else { return(new NegotiateResponseNotSupported()); } } else { // [MS-CIFS] An SMB_COM_NEGOTIATE exchange MUST be completed before any other SMB messages are sent to the server header.Status = NTStatus.STATUS_INVALID_SMB; return(new ErrorResponse(command.CommandName)); } } else if (command is NegotiateRequest) { // There MUST be only one SMB_COM_NEGOTIATE exchange per SMB connection. // Subsequent SMB_COM_NEGOTIATE requests received by the server MUST be rejected with error responses. header.Status = NTStatus.STATUS_INVALID_SMB; return(new ErrorResponse(command.CommandName)); } else { return(ProcessSMB1Command(header, command, (SMB1ConnectionState)state)); } }