Esempio n. 1
0
        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)
            {
                state.LogToServer(Severity.Verbose, "Write 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, "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)
            {
                state.LogToServer(Severity.Verbose, "Write to '{0}{1}' failed. NTStatus: {2}. (FileId: {3})", share.Name, openFile.Path, writeStatus, request.FileId.Volatile);
                return(new ErrorResponse(request.CommandName, writeStatus));
            }
            WriteResponse response = new WriteResponse();

            response.Count = (uint)numberOfBytesWritten;
            return(response);
        }
Esempio n. 2
0
        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)
            {
                state.LogToServer(Severity.Verbose, "Read 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, "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)
            {
                state.LogToServer(Severity.Verbose, "Read from '{0}{1}' failed. NTStatus: {2}. (FileId: {3})", share.Name, openFile.Path, readStatus, request.FileId.Volatile);
                return(new ErrorResponse(request.CommandName, readStatus));
            }
            ReadResponse response = new ReadResponse();

            response.Data = data;
            return(response);
        }
Esempio n. 3
0
        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);
            }
        }
Esempio n. 4
0
        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 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);
        }
        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());
        }
Esempio n. 7
0
        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());
        }
Esempio n. 8
0
        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));
        }
Esempio n. 9
0
        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);
        }
Esempio n. 10
0
        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);
                    }
                }
            }
        }
Esempio n. 11
0
        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));
        }
Esempio n. 12
0
        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);
            }
        }
Esempio n. 13
0
        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);
            }
        }
Esempio n. 14
0
        /// <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);
            }
        }
Esempio n. 15
0
        internal static SMB2Command GetSessionSetupResponse(SessionSetupRequest request, GSSProvider securityProvider, SMB2ConnectionState state)
        {
            // [MS-SMB2] Windows [..] will also accept raw Kerberos messages and implicit NTLM messages as part of GSS authentication.
            SessionSetupResponse response = new SessionSetupResponse();

            byte[]   outputToken;
            NTStatus status = securityProvider.AcceptSecurityContext(ref state.AuthenticationContext, request.SecurityBuffer, out outputToken);

            if (status != NTStatus.STATUS_SUCCESS && status != NTStatus.SEC_I_CONTINUE_NEEDED)
            {
                string userName    = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string;
                string domainName  = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.DomainName) as string;
                string machineName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.MachineName) as string;
                string osVersion   = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.OSVersion) as string;
                state.LogToServer(Severity.Information, "Session Setup: User '{0}' failed authentication (Domain: '{1}', Workstation: '{2}', OS version: '{3}'), NTStatus: {4}", userName, domainName, machineName, osVersion, status);
                return(new ErrorResponse(request.CommandName, status));
            }

            if (outputToken != null)
            {
                response.SecurityBuffer = outputToken;
            }

            // According to [MS-SMB2] 3.3.5.5.3, response.Header.SessionID must be allocated if the server returns STATUS_MORE_PROCESSING_REQUIRED
            if (request.Header.SessionID == 0)
            {
                ulong?sessionID = state.AllocateSessionID();
                if (!sessionID.HasValue)
                {
                    return(new ErrorResponse(request.CommandName, NTStatus.STATUS_TOO_MANY_SESSIONS));
                }
                response.Header.SessionID = sessionID.Value;
            }

            if (status == NTStatus.SEC_I_CONTINUE_NEEDED)
            {
                response.Header.Status = NTStatus.STATUS_MORE_PROCESSING_REQUIRED;
            }
            else // status == STATUS_SUCCESS
            {
                string userName    = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.UserName) as string;
                string domainName  = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.DomainName) as string;
                string machineName = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.MachineName) as string;
                string osVersion   = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.OSVersion) as string;
                byte[] sessionKey  = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.SessionKey) as byte[];
                object accessToken = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.AccessToken);
                bool?  isGuest     = securityProvider.GetContextAttribute(state.AuthenticationContext, GSSAttributeName.IsGuest) as bool?;
                if (!isGuest.HasValue || !isGuest.Value)
                {
                    state.LogToServer(Severity.Information, "Session Setup: User '{0}' authenticated successfully (Domain: '{1}', Workstation: '{2}', OS version: '{3}').", userName, domainName, machineName, osVersion);
                    bool        signingRequired = (request.SecurityMode & SecurityMode.SigningRequired) > 0;
                    SMB2Dialect smb2Dialect     = SMBServer.ToSMB2Dialect(state.Dialect);
                    byte[]      signingKey      = SMB2Cryptography.GenerateSigningKey(sessionKey, smb2Dialect, null);
                    state.CreateSession(request.Header.SessionID, userName, machineName, sessionKey, accessToken, signingRequired, signingKey);
                }
                else
                {
                    state.LogToServer(Severity.Information, "Session Setup: User '{0}' failed authentication (Domain: '{1}', Workstation: '{2}', OS version: '{3}'), logged in as guest.", userName, domainName, machineName, osVersion);
                    state.CreateSession(request.Header.SessionID, "Guest", machineName, sessionKey, accessToken, false, null);
                    response.SessionFlags = SessionFlags.IsGuest;
                }
            }
            return(response);
        }
Esempio n. 16
0
        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());
        }
Esempio n. 17
0
        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));
        }
Esempio n. 18
0
        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);
        }
Esempio n. 20
0
        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);
        }