/// <summary> /// May return null /// </summary> private SMB2Command ProcessSMB2Command(SMB2Command command, ref ConnectionState state) { if (state.Dialect == SMBDialect.NotSet) { if (command is NegotiateRequest) { NegotiateRequest request = (NegotiateRequest)command; SMB2Command response = NegotiateHelper.GetNegotiateResponse(request, m_securityProvider, state, m_serverGuid, m_serverStartTime); if (state.Dialect != SMBDialect.NotSet) { state = new SMB2ConnectionState(state); m_connectionManager.AddConnection(state); } return(response); } else { // [MS-SMB2] If the request being received is not an SMB2 NEGOTIATE Request [..] // and Connection.NegotiateDialect is 0xFFFF or 0x02FF, the server MUST // disconnect the connection. state.LogToServer(Severity.Debug, "Invalid Connection State for command {0}", command.CommandName.ToString()); state.ClientSocket.Close(); return(null); } } else if (command is NegotiateRequest) { // [MS-SMB2] If Connection.NegotiateDialect is 0x0202, 0x0210, 0x0300, 0x0302, or 0x0311, // the server MUST disconnect the connection. state.LogToServer(Severity.Debug, "Rejecting NegotiateRequest. NegotiateDialect is already set"); state.ClientSocket.Close(); return(null); } else { return(ProcessSMB2Command(command, (SMB2ConnectionState)state)); } }
private SMB2Command ProcessSMB2Command(SMB2Command command, SMB2ConnectionState state) { if (command is SessionSetupRequest) { return(SessionSetupHelper.GetSessionSetupResponse((SessionSetupRequest)command, m_securityProvider, state)); } else if (command is EchoRequest) { return(new EchoResponse()); } else { SMB2Session session = state.GetSession(command.Header.SessionID); if (session == null) { return(new ErrorResponse(command.CommandName, NTStatus.STATUS_USER_SESSION_DELETED)); } if (command is TreeConnectRequest) { return(TreeConnectHelper.GetTreeConnectResponse((TreeConnectRequest)command, state, m_services, m_shares)); } else if (command is LogoffRequest) { state.LogToServer(Severity.Information, "Logoff: User '{0}' logged off. (SessionID: {1})", session.UserName, command.Header.SessionID); m_securityProvider.DeleteSecurityContext(ref session.SecurityContext.AuthenticationContext); state.RemoveSession(command.Header.SessionID); return(new LogoffResponse()); } else if (command.Header.IsAsync) { // TreeID will not be present in an ASYNC header if (command is CancelRequest) { return(CancelHelper.GetCancelResponse((CancelRequest)command, state)); } } else { ISMBShare share = session.GetConnectedTree(command.Header.TreeID); if (share == null) { state.LogToServer(Severity.Verbose, "{0} failed. Invalid TreeID (SessionID: {1}, TreeID: {2}).", command.CommandName, command.Header.SessionID, command.Header.TreeID); return(new ErrorResponse(command.CommandName, NTStatus.STATUS_NETWORK_NAME_DELETED)); } if (command is TreeDisconnectRequest) { return(TreeConnectHelper.GetTreeDisconnectResponse((TreeDisconnectRequest)command, share, state)); } else if (command is CreateRequest) { return(CreateHelper.GetCreateResponse((CreateRequest)command, share, state)); } else if (command is QueryInfoRequest) { return(QueryInfoHelper.GetQueryInfoResponse((QueryInfoRequest)command, share, state)); } else if (command is SetInfoRequest) { return(SetInfoHelper.GetSetInfoResponse((SetInfoRequest)command, share, state)); } else if (command is QueryDirectoryRequest) { return(QueryDirectoryHelper.GetQueryDirectoryResponse((QueryDirectoryRequest)command, share, state)); } else if (command is ReadRequest) { return(ReadWriteResponseHelper.GetReadResponse((ReadRequest)command, share, state)); } else if (command is WriteRequest) { return(ReadWriteResponseHelper.GetWriteResponse((WriteRequest)command, share, state)); } else if (command is LockRequest) { return(LockHelper.GetLockResponse((LockRequest)command, share, state)); } else if (command is FlushRequest) { return(ReadWriteResponseHelper.GetFlushResponse((FlushRequest)command, share, state)); } else if (command is CloseRequest) { return(CloseHelper.GetCloseResponse((CloseRequest)command, share, state)); } else if (command is IOCtlRequest) { return(IOCtlHelper.GetIOCtlResponse((IOCtlRequest)command, share, state)); } else if (command is CancelRequest) { return(CancelHelper.GetCancelResponse((CancelRequest)command, state)); } else if (command is ChangeNotifyRequest) { return(ChangeNotifyHelper.GetChangeNotifyInterimResponse((ChangeNotifyRequest)command, share, state)); } } } return(new ErrorResponse(command.CommandName, NTStatus.STATUS_NOT_SUPPORTED)); }
private void ProcessPacket(SessionPacket packet, ref ConnectionState state) { 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 || state.Dialect == SMBDialect.SMB300)); if (SMB1Header.IsValidSMB1Header(packet.Trailer)) { if (!acceptSMB1) { state.LogToServer(Severity.Verbose, "Rejected SMB1 message"); state.ClientSocket.Close(); return; } SMB1Message message = null; try { message = SMB1Message.GetSMB1Message(packet.Trailer); } catch (Exception ex) { state.LogToServer(Severity.Warning, "Invalid SMB1 message: " + ex.Message); state.ClientSocket.Close(); 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_transport, 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.Close(); } } else if (SMB2Header.IsValidSMB2Header(packet.Trailer)) { if (!acceptSMB2) { state.LogToServer(Severity.Verbose, "Rejected SMB2 message"); state.ClientSocket.Close(); 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.Close(); 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.Close(); } } else 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 { state.LogToServer(Severity.Warning, "Inappropriate NetBIOS session packet"); state.ClientSocket.Close(); return; } }
private SMB2Command ProcessSMB2Command(SMB2Command command, SMB2ConnectionState state) { if (command is SessionSetupRequest) { return(SessionSetupHelper.GetSessionSetupResponse((SessionSetupRequest)command, m_securityProvider, state)); } else if (command is EchoRequest) { return(new EchoResponse()); } else { SMB2Session session = state.GetSession(command.Header.SessionID); if (session == null) { return(new ErrorResponse(command.CommandName, NTStatus.STATUS_USER_SESSION_DELETED)); } if (command is TreeConnectRequest) { return(TreeConnectHelper.GetTreeConnectResponse((TreeConnectRequest)command, state, m_services, m_shares)); } else if (command is LogoffRequest) { state.LogToServer(Severity.Information, "Logoff: User '{0}' logged off.", session.UserName); m_securityProvider.DeleteSecurityContext(ref session.SecurityContext.AuthenticationContext); state.RemoveSession(command.Header.SessionID); return(new LogoffResponse()); } else { // Cancel requests can have an ASYNC header (TreeID will not be present) if (command is CancelRequest) { if (command.Header.IsAsync && command.Header.AsyncID == 0) { ErrorResponse response = new ErrorResponse(command.CommandName, NTStatus.STATUS_CANCELLED); response.Header.IsAsync = true; return(response); } // [MS-SMB2] If a request is not found, the server MUST stop processing for this cancel request. No response is sent. return(null); } ISMBShare share = session.GetConnectedTree(command.Header.TreeID); if (share == null) { return(new ErrorResponse(command.CommandName, NTStatus.STATUS_NETWORK_NAME_DELETED)); } if (command is TreeDisconnectRequest) { return(TreeConnectHelper.GetTreeDisconnectResponse((TreeDisconnectRequest)command, share, state)); } else if (command is CreateRequest) { return(CreateHelper.GetCreateResponse((CreateRequest)command, share, state)); } else if (command is QueryInfoRequest) { return(QueryInfoHelper.GetQueryInfoResponse((QueryInfoRequest)command, share, state)); } else if (command is SetInfoRequest) { return(SetInfoHelper.GetSetInfoResponse((SetInfoRequest)command, share, state)); } else if (command is QueryDirectoryRequest) { return(QueryDirectoryHelper.GetQueryDirectoryResponse((QueryDirectoryRequest)command, share, state)); } else if (command is ReadRequest) { return(ReadWriteResponseHelper.GetReadResponse((ReadRequest)command, share, state)); } else if (command is WriteRequest) { return(ReadWriteResponseHelper.GetWriteResponse((WriteRequest)command, share, state)); } else if (command is FlushRequest) { FlushRequest request = (FlushRequest)command; OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } NTStatus status = share.FileStore.FlushFileBuffers(openFile.Handle); if (status != NTStatus.STATUS_SUCCESS) { return(new ErrorResponse(request.CommandName, status)); } return(new FlushResponse()); } else if (command is CloseRequest) { return(CloseHelper.GetCloseResponse((CloseRequest)command, share, state)); } else if (command is IOCtlRequest) { return(IOCtlHelper.GetIOCtlResponse((IOCtlRequest)command, share, state)); } else if (command is ChangeNotifyRequest) { // [MS-SMB2] If the underlying object store does not support change notifications, the server MUST fail this request with STATUS_NOT_SUPPORTED ErrorResponse response = new ErrorResponse(command.CommandName, NTStatus.STATUS_NOT_SUPPORTED); // Windows 7 / 8 / 10 will infinitely retry sending ChangeNotify requests if the response does not have SMB2_FLAGS_ASYNC_COMMAND set. // Note: NoRemoteChangeNotify can be set in the registry to prevent the client from sending ChangeNotify requests altogether. response.Header.IsAsync = true; return(response); } } } return(new ErrorResponse(command.CommandName, NTStatus.STATUS_NOT_SUPPORTED)); }