internal static SMB2Command GetReadResponse(ReadRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } if (share is FileSystemShare) { if (!((FileSystemShare)share).HasReadAccess(session.SecurityContext, openFile.Path)) { state.LogToServer(Severity.Verbose, "Read from '{0}{1}' failed. User '{2}' was denied access.", share.Name, openFile.Path, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } byte[] data; NTStatus readStatus = share.FileStore.ReadFile(out data, openFile.Handle, (long)request.Offset, (int)request.ReadLength); if (readStatus != NTStatus.STATUS_SUCCESS) { return(new ErrorResponse(request.CommandName, readStatus)); } ReadResponse response = new ReadResponse(); response.Data = data; return(response); }
internal static SMB2Command GetWriteResponse(WriteRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } if (share is FileSystemShare) { if (!((FileSystemShare)share).HasWriteAccess(session.SecurityContext, openFile.Path)) { state.LogToServer(Severity.Verbose, "Write to '{0}{1}' failed. User '{2}' was denied access.", share.Name, openFile.Path, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } int numberOfBytesWritten; NTStatus writeStatus = share.FileStore.WriteFile(out numberOfBytesWritten, openFile.Handle, (long)request.Offset, request.Data); if (writeStatus != NTStatus.STATUS_SUCCESS) { return(new ErrorResponse(request.CommandName, writeStatus)); } WriteResponse response = new WriteResponse(); response.Count = (uint)numberOfBytesWritten; return(response); }
internal static SMB2Command GetQueryInfoResponse(QueryInfoRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); if (request.InfoType == InfoType.File) { OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } if (share is FileSystemShare) { if (!((FileSystemShare)share).HasReadAccess(session.SecurityContext, openFile.Path)) { state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}{1}' failed. User '{2}' was denied access.", share.Name, openFile.Path, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } FileInformation fileInformation; NTStatus queryStatus = share.FileStore.GetFileInformation(out fileInformation, openFile.Handle, request.FileInformationClass); if (queryStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}{1}' failed. Information class: {2}, NTStatus: {3}", share.Name, openFile.Path, request.FileInformationClass, queryStatus); return(new ErrorResponse(request.CommandName, queryStatus)); } state.LogToServer(Severity.Information, "GetFileInformation on '{0}{1}' succeeded. Information class: {2}", share.Name, openFile.Path, request.FileInformationClass); QueryInfoResponse response = new QueryInfoResponse(); response.SetFileInformation(fileInformation); return(response); } else if (request.InfoType == InfoType.FileSystem) { if (share is FileSystemShare) { if (!((FileSystemShare)share).HasReadAccess(session.SecurityContext, @"\")) { state.LogToServer(Severity.Verbose, "GetFileSystemInformation on '{0}' failed. User '{1}' was denied access.", share.Name, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } FileSystemInformation fileSystemInformation; NTStatus queryStatus = share.FileStore.GetFileSystemInformation(out fileSystemInformation, request.FileSystemInformationClass); if (queryStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "GetFileSystemInformation on '{0}' failed. Information class: {1}, NTStatus: {2}", share.Name, request.FileSystemInformationClass, queryStatus); return(new ErrorResponse(request.CommandName, queryStatus)); } state.LogToServer(Severity.Information, "GetFileSystemInformation on '{0} succeeded. Information class: {1}", share.Name, request.FileSystemInformationClass); QueryInfoResponse response = new QueryInfoResponse(); response.SetFileSystemInformation(fileSystemInformation); return(response); } } return(new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED)); }
internal static SMB2Command GetTreeDisconnectResponse(TreeDisconnectRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); session.DisconnectTree(request.Header.TreeID); state.LogToServer(Severity.Information, "Tree Disconnect: User '{0}' disconnected from '{1}' (SessionID: {2}, TreeID: {3})", session.UserName, share.Name, request.Header.SessionID, request.Header.TreeID); return(new TreeDisconnectResponse()); }
internal static SMB2Command GetCreateResponse(CreateRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); string path = request.Name; if (!path.StartsWith(@"\")) { path = @"\" + path; } FileAccess createAccess = NTFileStoreHelper.ToCreateFileAccess(request.DesiredAccess, request.CreateDisposition); if (share is FileSystemShare) { if (!((FileSystemShare)share).HasAccess(session.SecurityContext, path, createAccess)) { state.LogToServer(Severity.Verbose, "Create: Opening '{0}{1}' failed. User '{2}' was denied access.", share.Name, path, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } object handle; FileStatus fileStatus; // GetFileInformation/FileNetworkOpenInformation requires FILE_READ_ATTRIBUTES AccessMask desiredAccess = request.DesiredAccess | (AccessMask)FileAccessMask.FILE_READ_ATTRIBUTES; NTStatus createStatus = share.FileStore.CreateFile(out handle, out fileStatus, path, desiredAccess, request.FileAttributes, request.ShareAccess, request.CreateDisposition, request.CreateOptions, session.SecurityContext); if (createStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "Create: Opening '{0}{1}' failed. NTStatus: {2}.", share.Name, path, createStatus); return(new ErrorResponse(request.CommandName, createStatus)); } FileAccess fileAccess = NTFileStoreHelper.ToFileAccess(desiredAccess); FileID? fileID = session.AddOpenFile(request.Header.TreeID, share.Name, path, handle, fileAccess); if (fileID == null) { share.FileStore.CloseFile(handle); state.LogToServer(Severity.Verbose, "Create: Opening '{0}{1}' failed. Too many open files.", share.Name, path); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_OPENED_FILES)); } string fileAccessString = fileAccess.ToString().Replace(", ", "|"); string shareAccessString = request.ShareAccess.ToString().Replace(", ", "|"); state.LogToServer(Severity.Verbose, "Create: Opened '{0}{1}', FileAccess: {2}, ShareAccess: {3}. (SessionID: {4}, TreeID: {5}, FileId: {6})", share.Name, path, fileAccessString, shareAccessString, request.Header.SessionID, request.Header.TreeID, fileID.Value.Volatile); if (share is NamedPipeShare) { return(CreateResponseForNamedPipe(fileID.Value, FileStatus.FILE_OPENED)); } else { FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(share.FileStore, handle); CreateResponse response = CreateResponseFromFileSystemEntry(fileInfo, fileID.Value, fileStatus); return(response); } }
internal static SMB2Command GetCreateResponse(CreateRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); string path = request.Name; if (!path.StartsWith(@"\")) { path = @"\" + path; } FileAccess createAccess = NTFileStoreHelper.ToCreateFileAccess(request.DesiredAccess, request.CreateDisposition); if (share is FileSystemShare) { if (!((FileSystemShare)share).HasAccess(session.SecurityContext, path, createAccess)) { state.LogToServer(Severity.Verbose, "Create: Opening '{0}{1}' failed. User '{2}' was denied access.", share.Name, path, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } object handle; FileStatus fileStatus; NTStatus createStatus = share.FileStore.CreateFile(out handle, out fileStatus, path, request.DesiredAccess, request.ShareAccess, request.CreateDisposition, request.CreateOptions, session.SecurityContext); if (createStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "Create: Opening '{0}{1}' failed. NTStatus: '{2}'.", share.Name, path, createStatus); return(new ErrorResponse(request.CommandName, createStatus)); } state.LogToServer(Severity.Verbose, "Create: Opened '{0}{1}'.", share.Name, path); FileID?fileID = session.AddOpenFile(request.Header.TreeID, path, handle); if (fileID == null) { share.FileStore.CloseFile(handle); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_OPENED_FILES)); } if (share is NamedPipeShare) { return(CreateResponseForNamedPipe(fileID.Value, FileStatus.FILE_OPENED)); } else { FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(share.FileStore, handle); CreateResponse response = CreateResponseFromFileSystemEntry(fileInfo, fileID.Value, fileStatus); if (request.RequestedOplockLevel == OplockLevel.Batch) { response.OplockLevel = OplockLevel.Batch; } return(response); } }
internal static SMB2Command GetSetInfoResponse(SetInfoRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); if (request.InfoType == InfoType.File) { OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } if (share is FileSystemShare) { if (!((FileSystemShare)share).HasWriteAccess(session.SecurityContext, openFile.Path)) { state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}{1}' failed. User '{2}' was denied access.", share.Name, openFile.Path, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } FileInformation information; try { information = FileInformation.GetFileInformation(request.Buffer, 0, request.FileInformationClass); } catch (UnsupportedInformationLevelException) { state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}{1}' failed. Information class: {2}, NTStatus: STATUS_INVALID_INFO_CLASS", share.Name, openFile.Path, request.FileInformationClass); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_INFO_CLASS)); } catch (NotImplementedException) { state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}{1}' failed. Information class: {2}, NTStatus: STATUS_NOT_SUPPORTED", share.Name, openFile.Path, request.FileInformationClass); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED)); } catch (Exception) { state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}{1}' failed. Information class: {2}, NTStatus: STATUS_INVALID_PARAMETER", share.Name, openFile.Path, request.FileInformationClass); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER)); } NTStatus status = share.FileStore.SetFileInformation(openFile.Handle, information); if (status != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}{1}' failed. Information class: {2}, NTStatus: {3}", share.Name, openFile.Path, request.FileInformationClass, status); return(new ErrorResponse(request.CommandName, status)); } state.LogToServer(Severity.Information, "SetFileInformation on '{0}{1}' succeeded. Information class: {2}", share.Name, openFile.Path, request.FileInformationClass); return(new SetInfoResponse()); } return(new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED)); }
internal static SMB2Command GetIOCtlResponse(IOCtlRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); if (request.CtlCode == (uint)IoControlCode.FSCTL_DFS_GET_REFERRALS || request.CtlCode == (uint)IoControlCode.FSCTL_DFS_GET_REFERRALS_EX) { // [MS-SMB2] 3.3.5.15.2 Handling a DFS Referral Information Request return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FS_DRIVER_REQUIRED)); } OpenFileObject openFile = session.GetOpenFileObject(request.FileId); object handle; if (openFile == null) { if (request.CtlCode == (uint)IoControlCode.FSCTL_PIPE_WAIT || request.CtlCode == (uint)IoControlCode.FSCTL_VALIDATE_NEGOTIATE_INFO || request.CtlCode == (uint)IoControlCode.FSCTL_QUERY_NETWORK_INTERFACE_INFO) { // [MS-SMB2] 3.3.5.1.5 - FSCTL_PIPE_WAIT / FSCTL_QUERY_NETWORK_INTERFACE_INFO / // FSCTL_VALIDATE_NEGOTIATE_INFO requests have FileId set to 0xFFFFFFFFFFFFFFFF. handle = null; } else { return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } } else { handle = openFile.Handle; } int maxOutputLength = (int)request.MaxOutputResponse; byte[] output; NTStatus status = share.FileStore.DeviceIOControl(handle, request.CtlCode, request.Input, out output, maxOutputLength); if (status != NTStatus.STATUS_SUCCESS) { return(new ErrorResponse(request.CommandName, status)); } IOCtlResponse response = new IOCtlResponse(); response.CtlCode = request.CtlCode; response.FileId = request.FileId; response.Output = output; return(response); }
internal static SMB2Command GetTreeConnectResponse(TreeConnectRequest request, SMB2ConnectionState state, NamedPipeShare services, SMBShareCollection shares) { SMB2Session session = state.GetSession(request.Header.SessionID); TreeConnectResponse response = new TreeConnectResponse(); string shareName = ServerPathUtils.GetShareName(request.Path); ISMBShare share; ShareType shareType; ShareFlags shareFlags; if (String.Equals(shareName, NamedPipeShare.NamedPipeShareName, StringComparison.OrdinalIgnoreCase)) { share = services; shareType = ShareType.Pipe; shareFlags = ShareFlags.NoCaching; } else { share = shares.GetShareFromName(shareName); if (share == null) { return(new ErrorResponse(request.CommandName, NTStatus.STATUS_OBJECT_PATH_NOT_FOUND)); } shareType = ShareType.Disk; shareFlags = GetShareCachingFlags(((FileSystemShare)share).CachingPolicy); if (!((FileSystemShare)share).HasReadAccess(session.SecurityContext, @"\")) { state.LogToServer(Severity.Verbose, "Tree Connect to '{0}' failed. User '{1}' was denied access.", share.Name, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } uint?treeID = session.AddConnectedTree(share); if (!treeID.HasValue) { return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INSUFF_SERVER_RESOURCES)); } state.LogToServer(Severity.Information, "Tree Connect: User '{0}' connected to '{1}' (SessionID: {2}, TreeID: {3})", session.UserName, share.Name, request.Header.SessionID, treeID.Value); response.Header.TreeID = treeID.Value; response.ShareType = shareType; response.ShareFlags = shareFlags; response.MaximalAccess = (AccessMask)(FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA | FileAccessMask.FILE_APPEND_DATA | FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA | FileAccessMask.FILE_EXECUTE | FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES) | AccessMask.DELETE | AccessMask.READ_CONTROL | AccessMask.WRITE_DAC | AccessMask.WRITE_OWNER | AccessMask.SYNCHRONIZE; return(response); }
private static void OnNotifyChangeCompleted(NTStatus status, byte[] buffer, object context) { SMB2AsyncContext asyncContext = (SMB2AsyncContext)context; // Wait until the interim response has been sent lock (asyncContext) { SMB2ConnectionState connection = asyncContext.Connection; connection.RemoveAsyncContext(asyncContext); SMB2Session session = connection.GetSession(asyncContext.SessionID); if (session != null) { OpenFileObject openFile = session.GetOpenFileObject(asyncContext.FileID); if (openFile != null) { connection.LogToServer(Severity.Verbose, "NotifyChange: Monitoring of '{0}{1}' completed. NTStatus: {2}. AsyncID: {3}", openFile.ShareName, openFile.Path, status, asyncContext.AsyncID); } if (status == NTStatus.STATUS_SUCCESS || status == NTStatus.STATUS_NOTIFY_CLEANUP || status == NTStatus.STATUS_NOTIFY_ENUM_DIR) { ChangeNotifyResponse response = new ChangeNotifyResponse(); response.Header.Status = status; response.Header.IsAsync = true; response.Header.IsSigned = session.SigningRequired; response.Header.AsyncID = asyncContext.AsyncID; response.Header.SessionID = asyncContext.SessionID; response.OutputBuffer = buffer; SMBServer.EnqueueResponse(connection, response); } else { // [MS-SMB2] If the object store returns an error, the server MUST fail the request with the error code received. ErrorResponse response = new ErrorResponse(SMB2CommandName.ChangeNotify); response.Header.Status = status; response.Header.IsAsync = true; response.Header.IsSigned = session.SigningRequired; response.Header.AsyncID = asyncContext.AsyncID; SMBServer.EnqueueResponse(connection, response); } } } }
internal static SMB2Command GetCancelResponse(CancelRequest request, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); if (request.Header.IsAsync) { SMB2AsyncContext context = state.GetAsyncContext(request.Header.AsyncID); if (context != null) { ISMBShare share = session.GetConnectedTree(context.TreeID); OpenFileObject openFile = session.GetOpenFileObject(context.FileID); NTStatus status = share.FileStore.Cancel(context.IORequest); if (openFile != null) { state.LogToServer(Severity.Information, "Cancel: Requested cancel on '{0}{1}'. NTStatus: {2}, AsyncID: {3}.", share.Name, openFile.Path, status, context.AsyncID); } if (status == NTStatus.STATUS_SUCCESS || status == NTStatus.STATUS_CANCELLED || status == NTStatus.STATUS_NOT_SUPPORTED) // See ChangeNotifyHelper.cs { state.RemoveAsyncContext(context); // [MS-SMB2] If the target request is successfully canceled, the target request MUST be failed by sending // an ERROR response packet [..] with the status field of the SMB2 header set to STATUS_CANCELLED. ErrorResponse response = new ErrorResponse(request.CommandName, NTStatus.STATUS_CANCELLED); response.Header.IsAsync = true; response.Header.AsyncID = context.AsyncID; return(response); } // [MS-SMB2] If the target request is not successfully canceled [..] no response is sent. // Note: Failing to respond might cause the client to disconnect the connection as per [MS-SMB2] 3.2.6.1 Request Expiration Timer Event return(null); } else { // [MS-SMB2] If a request is not found [..] no response is sent. return(null); } } else { // [MS-SMB2] the SMB2 CANCEL Request MUST use an ASYNC header for canceling requests that have received an interim response. // [MS-SMB2] If the target request is not successfully canceled [..] no response is sent. return(null); } }
internal static SMB2Command GetFlushResponse(FlushRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { state.LogToServer(Severity.Verbose, "Flush failed. Invalid FileId. (SessionID: {0}, TreeID: {1}, FileId: {2})", request.Header.SessionID, request.Header.TreeID, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } NTStatus status = share.FileStore.FlushFileBuffers(openFile.Handle); if (status != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "Flush '{0}{1}' failed. NTStatus: {2}. (FileId: {3})", share.Name, openFile.Path, status, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, status)); } return(new FlushResponse()); }
internal static SMB2Command GetCloseResponse(CloseRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { state.LogToServer(Severity.Verbose, "Close failed. Invalid FileId. (SessionID: {0}, TreeID: {1}, FileId: {2})", request.Header.SessionID, request.Header.TreeID, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } NTStatus closeStatus = share.FileStore.CloseFile(openFile.Handle); if (closeStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Information, "Close: Closing '{0}{1}' failed. NTStatus: {2}. (SessionID: {3}, TreeID: {4}, FileId: {5})", share.Name, openFile.Path, closeStatus, request.Header.SessionID, request.Header.TreeID, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, closeStatus)); } state.LogToServer(Severity.Information, "Close: Closed '{0}{1}'. (SessionID: {2}, TreeID: {3}, FileId: {4})", share.Name, openFile.Path, request.Header.SessionID, request.Header.TreeID, request.FileId.Volatile); session.RemoveOpenFile(request.FileId); CloseResponse response = new CloseResponse(); if (request.PostQueryAttributes) { FileNetworkOpenInformation fileInfo = NTFileStoreHelper.GetNetworkOpenInformation(share.FileStore, openFile.Path, session.SecurityContext); if (fileInfo != null) { response.CreationTime = fileInfo.CreationTime; response.LastAccessTime = fileInfo.LastAccessTime; response.LastWriteTime = fileInfo.LastWriteTime; response.ChangeTime = fileInfo.ChangeTime; response.AllocationSize = fileInfo.AllocationSize; response.EndofFile = fileInfo.EndOfFile; response.FileAttributes = fileInfo.FileAttributes; } } return(response); }
internal static SMB2Command GetChangeNotifyInterimResponse(ChangeNotifyRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); OpenFileObject openFile = session.GetOpenFileObject(request.FileId); bool watchTree = (request.Flags & ChangeNotifyFlags.WatchTree) > 0; SMB2AsyncContext context = state.CreateAsyncContext(request.FileId, state, request.Header.SessionID, request.Header.TreeID); // We have to make sure that we don't send an interim response after the final response. lock (context) { NTStatus status = share.FileStore.NotifyChange(out context.IORequest, openFile.Handle, request.CompletionFilter, watchTree, (int)request.OutputBufferLength, OnNotifyChangeCompleted, context); if (status == NTStatus.STATUS_PENDING) { state.LogToServer(Severity.Verbose, "NotifyChange: Monitoring of '{0}{1}' started. AsyncID: {2}.", share.Name, openFile.Path, context.AsyncID); } // [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(request.CommandName, status); // 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; response.Header.AsyncID = context.AsyncID; return(response); } }
/// <remarks> /// 'NoRemoteChangeNotify' can be set in the registry to prevent the client from sending ChangeNotify requests altogether. /// </remarks> internal static SMB2Command GetChangeNotifyInterimResponse(ChangeNotifyRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); OpenFileObject openFile = session.GetOpenFileObject(request.FileId); bool watchTree = (request.Flags & ChangeNotifyFlags.WatchTree) > 0; SMB2AsyncContext asyncContext = state.CreateAsyncContext(request.FileId, state, request.Header.SessionID, request.Header.TreeID); // We have to make sure that we don't send an interim response after the final response. lock (asyncContext) { NTStatus status = share.FileStore.NotifyChange(out asyncContext.IORequest, openFile.Handle, request.CompletionFilter, watchTree, (int)request.OutputBufferLength, OnNotifyChangeCompleted, asyncContext); if (status == NTStatus.STATUS_PENDING) { state.LogToServer(Severity.Verbose, "NotifyChange: Monitoring of '{0}{1}' started. AsyncID: {2}.", share.Name, openFile.Path, asyncContext.AsyncID); } else if (status == NTStatus.STATUS_NOT_SUPPORTED) { // [MS-SMB2] If the underlying object store does not support change notifications, the server MUST fail this request with STATUS_NOT_SUPPORTED. // Unfortunately, Windows 7 / 8 / 10 will immediately retry sending another ChangeNotify request upon getting STATUS_NOT_SUPPORTED, // To prevent flooding, we must return a valid interim response (Status set to STATUS_PENDING and SMB2_FLAGS_ASYNC_COMMAND bit is set in Flags). status = NTStatus.STATUS_PENDING; } else { state.RemoveAsyncContext(asyncContext); } ErrorResponse response = new ErrorResponse(request.CommandName, status); if (status == NTStatus.STATUS_PENDING) { response.Header.IsAsync = true; response.Header.AsyncID = asyncContext.AsyncID; } return(response); } }
internal static SMB2Command GetLockResponse(LockRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { state.LogToServer(Severity.Verbose, "Lock failed. Invalid FileId. (SessionID: {0}, TreeID: {1}, FileId: {2})", request.Header.SessionID, request.Header.TreeID, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } if (request.Locks.Count == 0) { // [MS-SMB2] The lock count MUST be greater than or equal to 1 state.LogToServer(Severity.Verbose, "Lock: Invalid number of locks, must be greater than 0."); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER)); } // [MS-SMB2] If the flags of the initial SMB2_LOCK_ELEMENT in the Locks array of the request has // SMB2_LOCKFLAG_UNLOCK set, the server MUST process the lock array as a series of unlocks. // Otherwise, it MUST process the lock array as a series of lock requests. bool unlock = request.Locks[0].Unlock; foreach (LockElement lockElement in request.Locks) { if (unlock) { if (lockElement.SharedLock || lockElement.ExclusiveLock) { state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: Lock in a series of unlocks."); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER)); } } else { if (lockElement.Unlock) { state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: Unlock in a series of locks."); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER)); } if (lockElement.SharedLock && lockElement.ExclusiveLock) { state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: SMB2_LOCKFLAG_SHARED_LOCK and SMB2_LOCKFLAG_EXCLUSIVE_LOCK are mutually exclusive."); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER)); } if (request.Locks.Count > 1 && !lockElement.FailImmediately) { state.LogToServer(Severity.Verbose, "Lock: Invalid parameter: SMB2_LOCKFLAG_FAIL_IMMEDIATELY not set in a series of locks."); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER)); } } } for (int lockIndex = 0; lockIndex < request.Locks.Count; lockIndex++) { LockElement lockElement = request.Locks[lockIndex]; if (unlock) { NTStatus status = share.FileStore.UnlockFile(openFile.Handle, (long)lockElement.Offset, (long)lockElement.Length); if (status != NTStatus.STATUS_SUCCESS) { // [MS-SMB2] If the unlock operation fails, the server MUST fail the operation with the error code received from the object store and stop processing further entries in the Locks array. state.LogToServer(Severity.Information, "Lock: Unlocking '{0}{1}' failed. Offset: {2}, Length: {3}. NTStatus: {4}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length, status); return(new ErrorResponse(request.CommandName, status)); } state.LogToServer(Severity.Information, "Lock: Unlocking '{0}{1}' succeeded. Offset: {2}, Length: {3}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length); } else { NTStatus status = share.FileStore.LockFile(openFile.Handle, (long)lockElement.Offset, (long)lockElement.Length, lockElement.ExclusiveLock); if (status != NTStatus.STATUS_SUCCESS) { // [MS-SMB2] If the lock operation fails, the server MUST unlock any ranges locked as part of processing the previous entries in the Locks array of this request. state.LogToServer(Severity.Information, "Lock: Locking '{0}{1}' failed. Offset: {2}, Length: {3}. NTStatus: {4}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length, status); for (int index = 0; index < lockIndex; index++) { share.FileStore.UnlockFile(openFile.Handle, (long)request.Locks[index].Offset, (long)request.Locks[index].Length); } return(new ErrorResponse(request.CommandName, status)); } state.LogToServer(Severity.Information, "Lock: Locking '{0}{1}' succeeded. Offset: {2}, Length: {3}.", share.Name, openFile.Path, lockElement.Offset, lockElement.Length); } } return(new LockResponse()); }
internal static SMB2Command GetQueryInfoResponse(QueryInfoRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); if (request.InfoType == InfoType.File) { OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { state.LogToServer(Severity.Verbose, "GetFileInformation failed. Invalid FileId. (SessionID: {0}, TreeID: {1}, FileId: {2})", request.Header.SessionID, request.Header.TreeID, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } if (share is FileSystemShare) { if (!((FileSystemShare)share).HasReadAccess(session.SecurityContext, openFile.Path)) { state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}{1}' failed. User '{2}' was denied access.", share.Name, openFile.Path, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } FileInformation fileInformation; NTStatus queryStatus = share.FileStore.GetFileInformation(out fileInformation, openFile.Handle, request.FileInformationClass); if (queryStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "GetFileInformation on '{0}{1}' failed. Information class: {2}, NTStatus: {3}. (FileId: {4})", share.Name, openFile.Path, request.FileInformationClass, queryStatus, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, queryStatus)); } state.LogToServer(Severity.Information, "GetFileInformation on '{0}{1}' succeeded. Information class: {2}. (FileId: {3})", share.Name, openFile.Path, request.FileInformationClass, request.FileId.Volatile); QueryInfoResponse response = new QueryInfoResponse(); response.SetFileInformation(fileInformation); return(response); } else if (request.InfoType == InfoType.FileSystem) { if (share is FileSystemShare) { if (!((FileSystemShare)share).HasReadAccess(session.SecurityContext, @"\")) { state.LogToServer(Severity.Verbose, "GetFileSystemInformation on '{0}' failed. User '{1}' was denied access.", share.Name, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } FileSystemInformation fileSystemInformation; NTStatus queryStatus = share.FileStore.GetFileSystemInformation(out fileSystemInformation, request.FileSystemInformationClass); if (queryStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "GetFileSystemInformation on '{0}' failed. Information class: {1}, NTStatus: {2}", share.Name, request.FileSystemInformationClass, queryStatus); return(new ErrorResponse(request.CommandName, queryStatus)); } state.LogToServer(Severity.Information, "GetFileSystemInformation on '{0}' succeeded. Information class: {1}", share.Name, request.FileSystemInformationClass); QueryInfoResponse response = new QueryInfoResponse(); response.SetFileSystemInformation(fileSystemInformation); return(response); } } else if (request.InfoType == InfoType.Security) { OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { state.LogToServer(Severity.Verbose, "GetSecurityInformation failed. Invalid FileId. (SessionID: {0}, TreeID: {1}, FileId: {2})", request.Header.SessionID, request.Header.TreeID, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } if (share is FileSystemShare) { if (!((FileSystemShare)share).HasReadAccess(session.SecurityContext, openFile.Path)) { state.LogToServer(Severity.Verbose, "GetSecurityInformation on '{0}{1}' failed. User '{2}' was denied access.", share.Name, openFile.Path, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } SecurityDescriptor securityDescriptor; NTStatus queryStatus = share.FileStore.GetSecurityInformation(out securityDescriptor, openFile.Handle, request.SecurityInformation); if (queryStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "GetSecurityInformation on '{0}{1}' failed. Security information: 0x{2}, NTStatus: {3}. (FileId: {4})", share.Name, openFile.Path, request.SecurityInformation.ToString("X"), queryStatus, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, queryStatus)); } if (securityDescriptor.Length > request.OutputBufferLength) { state.LogToServer(Severity.Information, "GetSecurityInformation on '{0}{1}' failed. Security information: 0x{2}, NTStatus: STATUS_BUFFER_TOO_SMALL. (FileId: {3})", share.Name, openFile.Path, request.SecurityInformation.ToString("X"), request.FileId.Volatile); byte[] errorData = LittleEndianConverter.GetBytes((uint)securityDescriptor.Length); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_BUFFER_TOO_SMALL, errorData)); } state.LogToServer(Severity.Information, "GetSecurityInformation on '{0}{1}' succeeded. Security information: 0x{2}. (FileId: {3})", share.Name, openFile.Path, request.SecurityInformation.ToString("X"), request.FileId.Volatile); QueryInfoResponse response = new QueryInfoResponse(); response.SetSecurityInformation(securityDescriptor); return(response); } return(new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED)); }
internal static SMB2Command GetSetInfoResponse(SetInfoRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); OpenFileObject openFile = null; if (request.InfoType == InfoType.File || request.InfoType == InfoType.Security) { openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { state.LogToServer(Severity.Verbose, "SetFileInformation failed. Invalid FileId. (SessionID: {0}, TreeID: {1}, FileId: {2})", request.Header.SessionID, request.Header.TreeID, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } if (share is FileSystemShare) { if (!((FileSystemShare)share).HasWriteAccess(session.SecurityContext, openFile.Path)) { state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}{1}' failed. User '{2}' was denied access.", share.Name, openFile.Path, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } } else if (request.InfoType == InfoType.FileSystem) { if (share is FileSystemShare) { if (!((FileSystemShare)share).HasWriteAccess(session.SecurityContext, @"\")) { state.LogToServer(Severity.Verbose, "SetFileSystemInformation on '{0}' failed. User '{1}' was denied access.", share.Name, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } } if (request.InfoType == InfoType.File) { FileInformation information; try { information = FileInformation.GetFileInformation(request.Buffer, 0, request.FileInformationClass); } catch (UnsupportedInformationLevelException) { state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}{1}' failed. Information class: {2}, NTStatus: STATUS_INVALID_INFO_CLASS.", share.Name, openFile.Path, request.FileInformationClass); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_INFO_CLASS)); } catch (NotImplementedException) { state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}{1}' failed. Information class: {2}, NTStatus: STATUS_NOT_SUPPORTED.", share.Name, openFile.Path, request.FileInformationClass); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED)); } catch (Exception) { state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}{1}' failed. Information class: {2}, NTStatus: STATUS_INVALID_PARAMETER.", share.Name, openFile.Path, request.FileInformationClass); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER)); } if ((share is FileSystemShare) && (information is FileRenameInformationType2)) { string newFileName = ((FileRenameInformationType2)information).FileName; if (!newFileName.StartsWith(@"\")) { newFileName = @"\" + newFileName; } if (!((FileSystemShare)share).HasWriteAccess(session.SecurityContext, newFileName)) { state.LogToServer(Severity.Verbose, "SetFileInformation: Rename '{0}{1}' to '{0}{2}' failed. User '{3}' was denied access.", share.Name, openFile.Path, newFileName, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } } NTStatus status = share.FileStore.SetFileInformation(openFile.Handle, information); if (status != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "SetFileInformation on '{0}{1}' failed. Information class: {2}, NTStatus: {3}. (FileId: {4})", share.Name, openFile.Path, request.FileInformationClass, status, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, status)); } if (information is FileRenameInformationType2) { string newFileName = ((FileRenameInformationType2)information).FileName; if (!newFileName.StartsWith(@"\")) { newFileName = @"\" + newFileName; } state.LogToServer(Severity.Verbose, "SetFileInformation: Rename '{0}{1}' to '{0}{2}' succeeded. (FileId: {3})", share.Name, openFile.Path, newFileName, request.FileId.Volatile); openFile.Path = newFileName; } else { state.LogToServer(Severity.Information, "SetFileInformation on '{0}{1}' succeeded. Information class: {2}. (FileId: {3})", share.Name, openFile.Path, request.FileInformationClass, request.FileId.Volatile); } return(new SetInfoResponse()); } else if (request.InfoType == InfoType.FileSystem) { FileSystemInformation fileSystemInformation; try { fileSystemInformation = FileSystemInformation.GetFileSystemInformation(request.Buffer, 0, request.FileSystemInformationClass); } catch (UnsupportedInformationLevelException) { state.LogToServer(Severity.Verbose, "SetFileSystemInformation on '{0}' failed. Information class: {1}, NTStatus: STATUS_INVALID_INFO_CLASS.", share.Name, request.FileSystemInformationClass); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_INFO_CLASS)); } catch (Exception) { state.LogToServer(Severity.Verbose, "SetFileSystemInformation on '{0}' failed. Information class: {1}, NTStatus: STATUS_INVALID_PARAMETER.", share.Name, request.FileSystemInformationClass); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER)); } NTStatus status = share.FileStore.SetFileSystemInformation(fileSystemInformation); if (status != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "SetFileSystemInformation on '{0}' failed. Information class: {1}, NTStatus: {2}.", share.Name, request.FileSystemInformationClass, status); return(new ErrorResponse(request.CommandName, status)); } state.LogToServer(Severity.Verbose, "SetFileSystemInformation on '{0}' succeeded. Information class: {1}.", share.Name, request.FileSystemInformationClass); return(new SetInfoResponse()); } else if (request.InfoType == InfoType.Security) { SecurityDescriptor securityDescriptor; try { securityDescriptor = new SecurityDescriptor(request.Buffer, 0); } catch { state.LogToServer(Severity.Verbose, "SetSecurityInformation on '{0}{1}' failed. NTStatus: STATUS_INVALID_PARAMETER.", share.Name, openFile.Path); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER)); } NTStatus status = share.FileStore.SetSecurityInformation(openFile, request.SecurityInformation, securityDescriptor); if (status != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "SetSecurityInformation on '{0}{1}' failed. Security information: 0x{2}, NTStatus: {3}. (FileId: {4})", share.Name, openFile.Path, request.SecurityInformation.ToString("X"), status, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, status)); } state.LogToServer(Severity.Information, "SetSecurityInformation on '{0}{1}' succeeded. Security information: 0x{2}. (FileId: {3})", share.Name, openFile.Path, request.SecurityInformation.ToString("X"), request.FileId.Volatile); return(new SetInfoResponse()); } return(new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED)); }
internal static SMB2Command GetQueryDirectoryResponse(QueryDirectoryRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { state.LogToServer(Severity.Verbose, "Query Directory failed. Invalid FileId. (SessionID: {0}, TreeID: {1}, FileId: {2})", request.Header.SessionID, request.Header.TreeID, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } if (!((FileSystemShare)share).HasReadAccess(session.SecurityContext, openFile.Path)) { state.LogToServer(Severity.Verbose, "Query Directory on '{0}{1}' failed. User '{2}' was denied access.", share.Name, openFile.Path, session.UserName); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_ACCESS_DENIED)); } FileSystemShare fileSystemShare = (FileSystemShare)share; FileID fileID = request.FileId; OpenSearch openSearch = session.GetOpenSearch(fileID); if (openSearch == null || request.Reopen) { if (request.Reopen) { session.RemoveOpenSearch(fileID); } List <QueryDirectoryFileInformation> entries; NTStatus searchStatus = share.FileStore.QueryDirectory(out entries, openFile.Handle, request.FileName, request.FileInformationClass); if (searchStatus != NTStatus.STATUS_SUCCESS) { state.LogToServer(Severity.Verbose, "Query Directory on '{0}{1}', Searched for '{2}', NTStatus: {3}", share.Name, openFile.Path, request.FileName, searchStatus.ToString()); return(new ErrorResponse(request.CommandName, searchStatus)); } state.LogToServer(Severity.Information, "Query Directory on '{0}{1}', Searched for '{2}', found {3} matching entries", share.Name, openFile.Path, request.FileName, entries.Count); openSearch = session.AddOpenSearch(fileID, entries, 0); } if (request.Restart || request.Reopen) { openSearch.EnumerationLocation = 0; } if (openSearch.Entries.Count == 0) { // [MS-SMB2] If there are no entries to return [..] the server MUST fail the request with STATUS_NO_SUCH_FILE. session.RemoveOpenSearch(fileID); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_NO_SUCH_FILE)); } if (openSearch.EnumerationLocation == openSearch.Entries.Count) { return(new ErrorResponse(request.CommandName, NTStatus.STATUS_NO_MORE_FILES)); } List <QueryDirectoryFileInformation> page = new List <QueryDirectoryFileInformation>(); int pageLength = 0; for (int index = openSearch.EnumerationLocation; index < openSearch.Entries.Count; index++) { QueryDirectoryFileInformation fileInformation = openSearch.Entries[index]; if (fileInformation.FileInformationClass != request.FileInformationClass) { // We do not support changing FileInformationClass during a search (unless SMB2_REOPEN is set). return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER)); } int entryLength = fileInformation.Length; if (pageLength + entryLength <= request.OutputBufferLength) { page.Add(fileInformation); int paddedLength = (int)Math.Ceiling((double)entryLength / 8) * 8; pageLength += paddedLength; openSearch.EnumerationLocation = index + 1; } else { break; } if (request.ReturnSingleEntry) { break; } } QueryDirectoryResponse response = new QueryDirectoryResponse(); response.SetFileInformationList(page); return(response); }
internal static SMB2Command GetIOCtlResponse(IOCtlRequest request, ISMBShare share, SMB2ConnectionState state) { SMB2Session session = state.GetSession(request.Header.SessionID); string ctlCode = Enum.IsDefined(typeof(IoControlCode), request.CtlCode) ? ((IoControlCode)request.CtlCode).ToString() : ("0x" + request.CtlCode.ToString("X8")); if (!request.IsFSCtl) { // [MS-SMB2] If the Flags field of the request is not SMB2_0_IOCTL_IS_FSCTL the server MUST fail the request with STATUS_NOT_SUPPORTED. state.LogToServer(Severity.Verbose, "IOCTL: Non-FSCTL requests are not supported. CTL Code: {0}", ctlCode); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_NOT_SUPPORTED)); } if (request.CtlCode == (uint)IoControlCode.FSCTL_DFS_GET_REFERRALS || request.CtlCode == (uint)IoControlCode.FSCTL_DFS_GET_REFERRALS_EX) { // [MS-SMB2] 3.3.5.15.2 Handling a DFS Referral Information Request state.LogToServer(Severity.Verbose, "IOCTL failed. CTL Code: {0}. NTStatus: STATUS_FS_DRIVER_REQUIRED.", ctlCode); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FS_DRIVER_REQUIRED)); } object handle; if (request.CtlCode == (uint)IoControlCode.FSCTL_PIPE_WAIT || request.CtlCode == (uint)IoControlCode.FSCTL_VALIDATE_NEGOTIATE_INFO || request.CtlCode == (uint)IoControlCode.FSCTL_QUERY_NETWORK_INTERFACE_INFO) { // [MS-SMB2] 3.3.5.15 - FSCTL_PIPE_WAIT / FSCTL_QUERY_NETWORK_INTERFACE_INFO / // FSCTL_VALIDATE_NEGOTIATE_INFO requests MUST have FileId set to 0xFFFFFFFFFFFFFFFF. if (request.FileId.Persistent != 0xFFFFFFFFFFFFFFFF || request.FileId.Volatile != 0xFFFFFFFFFFFFFFFF) { state.LogToServer(Severity.Verbose, "IOCTL failed. CTL Code: {0}. FileId MUST be 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", ctlCode); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_INVALID_PARAMETER)); } handle = null; } else { OpenFileObject openFile = session.GetOpenFileObject(request.FileId); if (openFile == null) { state.LogToServer(Severity.Verbose, "IOCTL failed. CTL Code: {0}. Invalid FileId. (SessionID: {1}, TreeID: {2}, FileId: {3})", ctlCode, request.Header.SessionID, request.Header.TreeID, request.FileId.Volatile); return(new ErrorResponse(request.CommandName, NTStatus.STATUS_FILE_CLOSED)); } handle = openFile.Handle; } int maxOutputLength = (int)request.MaxOutputResponse; byte[] output; NTStatus status = share.FileStore.DeviceIOControl(handle, request.CtlCode, request.Input, out output, maxOutputLength); if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.STATUS_BUFFER_OVERFLOW) { state.LogToServer(Severity.Verbose, "IOCTL failed. CTL Code: {0}. NTStatus: {1}.", ctlCode, status); return(new ErrorResponse(request.CommandName, status)); } state.LogToServer(Severity.Verbose, "IOCTL succeeded. CTL Code: {0}.", ctlCode); IOCtlResponse response = new IOCtlResponse(); response.Header.Status = status; response.CtlCode = request.CtlCode; response.FileId = request.FileId; response.Output = output; return(response); }