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; }
private static void EnqueueResponseChain(ConnectionState state, List <SMB2Command> responseChain) { byte[] signingKey = null; if (state is SMB2ConnectionState) { // Note: multiple sessions MAY be multiplexed on the same connection, so theoretically // we could have compounding unrelated requests from different sessions. // In practice however this is not a real problem. ulong sessionID = responseChain[0].Header.SessionID; if (sessionID != 0) { SMB2Session session = ((SMB2ConnectionState)state).GetSession(sessionID); if (session != null) { signingKey = session.SigningKey; } } } SessionMessagePacket packet = new SessionMessagePacket(); SMB2Dialect smb2Dialect = (signingKey != null) ? ToSMB2Dialect(state.Dialect) : SMB2Dialect.SMB2xx; packet.Trailer = SMB2Command.GetCommandChainBytes(responseChain, signingKey, smb2Dialect); state.SendQueue.Enqueue(packet); state.LogToServer(Severity.Verbose, "SMB2 response chain queued: Response count: {0}, First response: {1}, Packet length: {2}", responseChain.Count, responseChain[0].CommandName.ToString(), packet.Length); }
public SMB2Session CreateSession(ulong sessionID, string userName, string machineName, byte[] sessionKey, object accessToken) { SMB2Session session = new SMB2Session(this, sessionID, userName, machineName, sessionKey, accessToken); lock (m_sessions) { m_sessions.Add(sessionID, session); } return(session); }
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 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)); }