123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472 |
- /* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
- *
- * You can redistribute this program and/or modify it under the terms of
- * the GNU Lesser Public License as published by the Free Software Foundation,
- * either version 3 of the License, or (at your option) any later version.
- */
- using System;
- using System.Collections.Generic;
- using System.IO;
- using System.Text;
- using SMBLibrary.Services;
- using SMBLibrary.SMB1;
- using Utilities;
- namespace SMBLibrary.Server.SMB1
- {
- public class NTCreateHelper
- {
- internal static SMB1Command GetNTCreateResponse(SMB1Header header, NTCreateAndXRequest request, ISMBShare share, SMB1ConnectionState state)
- {
- bool isExtended = (request.Flags & NTCreateFlags.NT_CREATE_REQUEST_EXTENDED_RESPONSE) > 0;
- string path = request.FileName;
- if (share is NamedPipeShare)
- {
- RemoteService service = ((NamedPipeShare)share).GetService(path);
- if (service != null)
- {
- ushort? fileID = state.AddOpenedFile(path);
- if (!fileID.HasValue)
- {
- header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- if (isExtended)
- {
- return CreateResponseExtendedForNamedPipe(fileID.Value);
- }
- else
- {
- return CreateResponseForNamedPipe(fileID.Value);
- }
- }
- header.Status = NTStatus.STATUS_OBJECT_PATH_NOT_FOUND;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- else // FileSystemShare
- {
- FileSystemShare fileSystemShare = (FileSystemShare)share;
- string userName = state.GetConnectedUserName(header.UID);
- bool hasWriteAccess = fileSystemShare.HasWriteAccess(userName);
- IFileSystem fileSystem = fileSystemShare.FileSystem;
- bool forceDirectory = (request.CreateOptions & CreateOptions.FILE_DIRECTORY_FILE) > 0;
- bool forceFile = (request.CreateOptions & CreateOptions.FILE_NON_DIRECTORY_FILE) > 0;
- if (forceDirectory & (request.CreateDisposition != CreateDisposition.FILE_CREATE &&
- request.CreateDisposition != CreateDisposition.FILE_OPEN &&
- request.CreateDisposition != CreateDisposition.FILE_OPEN_IF))
- {
- header.Status = NTStatus.STATUS_INVALID_PARAMETER;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- // Windows will try to access named streams (alternate data streams) regardless of the FILE_NAMED_STREAMS flag, we need to prevent this behaviour.
- if (path.Contains(":"))
- {
- // Windows Server 2003 will return STATUS_OBJECT_NAME_NOT_FOUND
- header.Status = NTStatus.STATUS_NO_SUCH_FILE;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- FileSystemEntry entry = fileSystem.GetEntry(path);
- if (request.CreateDisposition == CreateDisposition.FILE_OPEN)
- {
- if (entry == null)
- {
- header.Status = NTStatus.STATUS_OBJECT_PATH_NOT_FOUND;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- if (entry.IsDirectory && forceFile)
- {
- header.Status = NTStatus.STATUS_FILE_IS_A_DIRECTORY;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- if (!entry.IsDirectory && forceDirectory)
- {
- // Not sure if that's the correct response
- header.Status = NTStatus.STATUS_OBJECT_NAME_COLLISION;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- }
- else if (request.CreateDisposition == CreateDisposition.FILE_CREATE)
- {
- if (entry != null)
- {
- // File already exists, fail the request
- state.LogToServer(Severity.Debug, "NTCreate: File '{0}' already exist", path);
- header.Status = NTStatus.STATUS_OBJECT_NAME_COLLISION;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- if (!hasWriteAccess)
- {
- header.Status = NTStatus.STATUS_ACCESS_DENIED;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- try
- {
- if (forceDirectory)
- {
- state.LogToServer(Severity.Information, "NTCreate: Creating directory '{0}'", path);
- entry = fileSystem.CreateDirectory(path);
- }
- else
- {
- state.LogToServer(Severity.Information, "NTCreate: Creating file '{0}'", path);
- entry = fileSystem.CreateFile(path);
- }
- }
- catch (IOException ex)
- {
- ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
- if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
- {
- state.LogToServer(Severity.Debug, "NTCreate: Sharing violation creating '{0}'", path);
- header.Status = NTStatus.STATUS_SHARING_VIOLATION;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- else
- {
- state.LogToServer(Severity.Debug, "NTCreate: Error creating '{0}'", path);
- header.Status = NTStatus.STATUS_DATA_ERROR;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- }
- catch (UnauthorizedAccessException)
- {
- state.LogToServer(Severity.Debug, "NTCreate: Error creating '{0}', Access Denied", path);
- header.Status = NTStatus.STATUS_ACCESS_DENIED;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- }
- else if (request.CreateDisposition == CreateDisposition.FILE_OPEN_IF ||
- request.CreateDisposition == CreateDisposition.FILE_OVERWRITE ||
- request.CreateDisposition == CreateDisposition.FILE_OVERWRITE_IF ||
- request.CreateDisposition == CreateDisposition.FILE_SUPERSEDE)
- {
- entry = fileSystem.GetEntry(path);
- if (entry == null)
- {
- if (request.CreateDisposition == CreateDisposition.FILE_OVERWRITE)
- {
- header.Status = NTStatus.STATUS_OBJECT_PATH_NOT_FOUND;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- if (!hasWriteAccess)
- {
- header.Status = NTStatus.STATUS_ACCESS_DENIED;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- try
- {
- if (forceDirectory)
- {
- state.LogToServer(Severity.Information, "NTCreate: Creating directory '{0}'", path);
- entry = fileSystem.CreateDirectory(path);
- }
- else
- {
- state.LogToServer(Severity.Information, "NTCreate: Creating file '{0}'", path);
- entry = fileSystem.CreateFile(path);
- }
- }
- catch (IOException ex)
- {
- ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
- if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
- {
- header.Status = NTStatus.STATUS_SHARING_VIOLATION;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- else
- {
- header.Status = NTStatus.STATUS_DATA_ERROR;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- }
- catch (UnauthorizedAccessException)
- {
- header.Status = NTStatus.STATUS_ACCESS_DENIED;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- }
- else
- {
- if (request.CreateDisposition == CreateDisposition.FILE_OVERWRITE ||
- request.CreateDisposition == CreateDisposition.FILE_OVERWRITE_IF ||
- request.CreateDisposition == CreateDisposition.FILE_SUPERSEDE)
- {
- if (!hasWriteAccess)
- {
- header.Status = NTStatus.STATUS_ACCESS_DENIED;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- // Truncate the file
- try
- {
- Stream temp = fileSystem.OpenFile(path, FileMode.Truncate, FileAccess.ReadWrite, FileShare.ReadWrite);
- temp.Close();
- }
- catch (IOException ex)
- {
- ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
- if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
- {
- header.Status = NTStatus.STATUS_SHARING_VIOLATION;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- else
- {
- header.Status = NTStatus.STATUS_DATA_ERROR;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- }
- catch (UnauthorizedAccessException)
- {
- header.Status = NTStatus.STATUS_ACCESS_DENIED;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- }
- }
- }
- else
- {
- throw new InvalidRequestException();
- }
- FileAccess fileAccess = ToFileAccess(request.DesiredAccess);
- FileShare fileShare = ToFileShare(request.ShareAccess);
- if (!hasWriteAccess && (fileAccess == FileAccess.Write || fileAccess == FileAccess.ReadWrite))
- {
- header.Status = NTStatus.STATUS_ACCESS_DENIED;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- Stream stream;
- bool deleteOnClose = false;
- if (fileAccess == (FileAccess)0 || entry.IsDirectory)
- {
- stream = null;
- }
- else
- {
- // When FILE_OPEN_REPARSE_POINT is specified, the operation should continue normally if the file is not a reparse point.
- // FILE_OPEN_REPARSE_POINT is a hint that the caller does not intend to actually read the file, with the exception
- // of a file copy operation (where the caller will attempt to simply copy the reparse point).
- deleteOnClose = (request.CreateOptions & CreateOptions.FILE_DELETE_ON_CLOSE) > 0;
- bool openReparsePoint = (request.CreateOptions & CreateOptions.FILE_OPEN_REPARSE_POINT) > 0;
- bool disableBuffering = (request.CreateOptions & CreateOptions.FILE_NO_INTERMEDIATE_BUFFERING) > 0;
- bool buffered = (request.CreateOptions & CreateOptions.FILE_SEQUENTIAL_ONLY) > 0 && !disableBuffering && !openReparsePoint;
- state.LogToServer(Severity.Verbose, "NTCreate: Opening '{0}', Access={1}, Share={2}, Buffered={3}", path, fileAccess, fileShare, buffered);
- try
- {
- stream = fileSystem.OpenFile(path, FileMode.Open, fileAccess, fileShare);
- }
- catch (IOException ex)
- {
- ushort errorCode = IOExceptionHelper.GetWin32ErrorCode(ex);
- if (errorCode == (ushort)Win32Error.ERROR_SHARING_VIOLATION)
- {
- state.LogToServer(Severity.Debug, "NTCreate: Sharing violation opening '{0}'", path);
- header.Status = NTStatus.STATUS_SHARING_VIOLATION;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- else
- {
- state.LogToServer(Severity.Debug, "NTCreate: Sharing violation opening '{0}', Data Error", path);
- header.Status = NTStatus.STATUS_DATA_ERROR;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- }
- catch (UnauthorizedAccessException)
- {
- state.LogToServer(Severity.Debug, "NTCreate: Sharing violation opening '{0}', Access Denied", path);
- header.Status = NTStatus.STATUS_ACCESS_DENIED;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
-
- if (buffered)
- {
- stream = new PrefetchedStream(stream);
- }
- }
- ushort? fileID = state.AddOpenedFile(path, stream, deleteOnClose);
- if (!fileID.HasValue)
- {
- header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES;
- return new ErrorResponse(CommandName.SMB_COM_NT_CREATE_ANDX);
- }
- if (isExtended)
- {
- NTCreateAndXResponseExtended response = CreateResponseExtendedFromFileSystemEntry(entry, fileID.Value);
- if ((request.Flags & NTCreateFlags.NT_CREATE_REQUEST_OPBATCH) > 0)
- {
- response.OpLockLevel = OpLockLevel.BatchOpLockGranted;
- }
- return response;
- }
- else
- {
- NTCreateAndXResponse response = CreateResponseFromFileSystemEntry(entry, fileID.Value);
- if ((request.Flags & NTCreateFlags.NT_CREATE_REQUEST_OPBATCH) > 0)
- {
- response.OpLockLevel = OpLockLevel.BatchOpLockGranted;
- }
- return response;
- }
- }
- }
- public static FileAccess ToFileAccess(DesiredAccess desiredAccess)
- {
- if ((desiredAccess & DesiredAccess.GENERIC_ALL) > 0 ||
- ((desiredAccess & DesiredAccess.FILE_READ_DATA) > 0 && (desiredAccess & DesiredAccess.FILE_WRITE_DATA) > 0) ||
- ((desiredAccess & DesiredAccess.FILE_READ_DATA) > 0 && (desiredAccess & DesiredAccess.FILE_APPEND_DATA) > 0))
- {
- return FileAccess.ReadWrite;
- }
- else if ((desiredAccess & DesiredAccess.GENERIC_WRITE) > 0 ||
- (desiredAccess & DesiredAccess.FILE_WRITE_DATA) > 0 ||
- (desiredAccess & DesiredAccess.FILE_APPEND_DATA) > 0)
- {
- return FileAccess.Write;
- }
- else if ((desiredAccess & DesiredAccess.FILE_READ_DATA) > 0)
- {
- return FileAccess.Read;
- }
- else
- {
- return (FileAccess)0;
- }
- }
- public static FileShare ToFileShare(ShareAccess shareAccess)
- {
- if ((shareAccess & ShareAccess.FILE_SHARE_READ) > 0 && (shareAccess & ShareAccess.FILE_SHARE_WRITE) > 0)
- {
- return FileShare.ReadWrite;
- }
- else if ((shareAccess & ShareAccess.FILE_SHARE_WRITE) > 0)
- {
- return FileShare.Write;
- }
- else if ((shareAccess & ShareAccess.FILE_SHARE_READ) > 0)
- {
- return FileShare.Read;
- }
- else if ((shareAccess & ShareAccess.FILE_SHARE_DELETE) > 0)
- {
- return FileShare.Delete;
- }
- else
- {
- return FileShare.None;
- }
- }
- private static NTCreateAndXResponse CreateResponseForNamedPipe(ushort fileID)
- {
- NTCreateAndXResponse response = new NTCreateAndXResponse();
- response.FID = fileID;
- response.CreateDisposition = CreateDisposition.FILE_OPEN;
- response.ExtFileAttributes = ExtendedFileAttributes.Normal;
- response.ResourceType = ResourceType.FileTypeMessageModePipe;
- response.NMPipeStatus.ICount = 255;
- response.NMPipeStatus.ReadMode = ReadMode.MessageMode;
- response.NMPipeStatus.NamedPipeType = NamedPipeType.MessageNodePipe;
- return response;
- }
- private static NTCreateAndXResponseExtended CreateResponseExtendedForNamedPipe(ushort fileID)
- {
- NTCreateAndXResponseExtended response = new NTCreateAndXResponseExtended();
- response.FID = fileID;
- response.CreateDisposition = CreateDisposition.FILE_OPEN;
- response.ExtFileAttributes = ExtendedFileAttributes.Normal;
- response.ResourceType = ResourceType.FileTypeMessageModePipe;
- NamedPipeStatus status = new NamedPipeStatus();
- status.ICount = 255;
- status.ReadMode = ReadMode.MessageMode;
- status.NamedPipeType = NamedPipeType.MessageNodePipe;
- response.NMPipeStatus = status;
- response.MaximalAccessRights.File = 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 |
- FileAccessMask.DELETE | FileAccessMask.READ_CONTROL | FileAccessMask.WRITE_DAC | FileAccessMask.WRITE_OWNER | FileAccessMask.SYNCHRONIZE;
- response.GuestMaximalAccessRights.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA |
- FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA |
- FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES |
- FileAccessMask.READ_CONTROL | FileAccessMask.SYNCHRONIZE;
- return response;
- }
- private static NTCreateAndXResponse CreateResponseFromFileSystemEntry(FileSystemEntry entry, ushort fileID)
- {
- NTCreateAndXResponse response = new NTCreateAndXResponse();
- if (entry.IsDirectory)
- {
- response.ExtFileAttributes = ExtendedFileAttributes.Directory;
- response.Directory = true;
- }
- else
- {
- response.ExtFileAttributes = ExtendedFileAttributes.Normal;
- }
- response.FID = fileID;
- response.CreateDisposition = CreateDisposition.FILE_OPEN;
- response.AllocationSize = InfoHelper.GetAllocationSize(entry.Size);
- response.EndOfFile = entry.Size;
- response.CreateTime = entry.CreationTime;
- response.LastAccessTime = entry.LastAccessTime;
- response.LastWriteTime = entry.LastWriteTime;
- response.LastChangeTime = entry.LastWriteTime;
- response.ResourceType = ResourceType.FileTypeDisk;
- return response;
- }
- private static NTCreateAndXResponseExtended CreateResponseExtendedFromFileSystemEntry(FileSystemEntry entry, ushort fileID)
- {
- NTCreateAndXResponseExtended response = new NTCreateAndXResponseExtended();
- if (entry.IsDirectory)
- {
- response.ExtFileAttributes = ExtendedFileAttributes.Directory;
- response.Directory = true;
- }
- else
- {
- response.ExtFileAttributes = ExtendedFileAttributes.Normal;
- }
- response.FID = fileID;
- response.CreateTime = entry.CreationTime;
- response.LastAccessTime = entry.LastAccessTime;
- response.LastWriteTime = entry.LastWriteTime;
- response.LastChangeTime = entry.LastWriteTime;
- response.CreateDisposition = CreateDisposition.FILE_OPEN;
- response.AllocationSize = InfoHelper.GetAllocationSize(entry.Size);
- response.EndOfFile = entry.Size;
- response.ResourceType = ResourceType.FileTypeDisk;
- response.FileStatus = FileStatus.NO_EAS | FileStatus.NO_SUBSTREAMS | FileStatus.NO_REPARSETAG;
- response.MaximalAccessRights.File = 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 |
- FileAccessMask.DELETE | FileAccessMask.READ_CONTROL | FileAccessMask.WRITE_DAC | FileAccessMask.WRITE_OWNER | FileAccessMask.SYNCHRONIZE;
- response.GuestMaximalAccessRights.File = FileAccessMask.FILE_READ_DATA | FileAccessMask.FILE_WRITE_DATA |
- FileAccessMask.FILE_READ_EA | FileAccessMask.FILE_WRITE_EA |
- FileAccessMask.FILE_READ_ATTRIBUTES | FileAccessMask.FILE_WRITE_ATTRIBUTES |
- FileAccessMask.READ_CONTROL | FileAccessMask.SYNCHRONIZE;
- return response;
- }
- }
- }
|