Browse Source

Added RPCPipeStream to improve read / write abstraction

Tal Aloni 8 years ago
parent
commit
211adcc78a

+ 1 - 0
SMBLibrary/SMBLibrary.csproj

@@ -139,6 +139,7 @@
     <Compile Include="Services\Enums\PlatformName.cs" />
     <Compile Include="Services\RemoteService.cs" />
     <Compile Include="Services\RemoteServiceHelper.cs" />
+    <Compile Include="Services\RPCPipeStream.cs" />
     <Compile Include="Services\ServerService\Enums\Permissions.cs" />
     <Compile Include="Services\ServerService\Enums\ServerType.cs" />
     <Compile Include="Services\ServerService\EnumStructures\ShareTypeExtended.cs" />

+ 0 - 21
SMBLibrary/Server/ConnectionState/SMB1ConnectionState.cs

@@ -28,8 +28,6 @@ namespace SMBLibrary.Server
         // Key is FID
         private Dictionary<ushort, OpenedFileObject> m_openedFiles = new Dictionary<ushort, OpenedFileObject>();
         private ushort m_nextFID = 1;
-        // Key is FID
-        private Dictionary<ushort, byte[]> m_namedPipeResponse = new Dictionary<ushort, byte[]>();
 
         // Key is PID
         public Dictionary<uint, ProcessStateObject> ProcessStateList = new Dictionary<uint, ProcessStateObject>();
@@ -262,25 +260,6 @@ namespace SMBLibrary.Server
             m_openedFiles.Remove(fileID);
         }
 
-        public void StoreNamedPipeReply(ushort fileID, byte[] response)
-        {
-            m_namedPipeResponse.Add(fileID, response);
-        }
-
-        public byte[] RetrieveNamedPipeReply(ushort fileID)
-        {
-            if (m_namedPipeResponse.ContainsKey(fileID))
-            {
-                byte[] result = m_namedPipeResponse[fileID];
-                m_namedPipeResponse.Remove(fileID);
-                return result;
-            }
-            else
-            {
-                return new byte[0];
-            }
-        }
-
         public uint? GetMaxDataCount(uint processID)
         {
             ProcessStateObject processState = GetProcessState(processID);

+ 3 - 3
SMBLibrary/Server/SMB1/NTCreateHelper.cs

@@ -22,10 +22,10 @@ namespace SMBLibrary.Server.SMB1
             string path = request.FileName;
             if (share is NamedPipeShare)
             {
-                RemoteService service = ((NamedPipeShare)share).GetService(path);
-                if (service != null)
+                Stream pipeStream = ((NamedPipeShare)share).OpenPipe(path);
+                if (pipeStream != null)
                 {
-                    ushort? fileID = state.AddOpenedFile(path);
+                    ushort? fileID = state.AddOpenedFile(path, pipeStream);
                     if (!fileID.HasValue)
                     {
                         header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES;

+ 3 - 3
SMBLibrary/Server/SMB1/OpenAndXHelper.cs

@@ -22,10 +22,10 @@ namespace SMBLibrary.Server.SMB1
             string path = request.FileName;
             if (share is NamedPipeShare)
             {
-                RemoteService service = ((NamedPipeShare)share).GetService(path);
-                if (service != null)
+                Stream pipeStream = ((NamedPipeShare)share).OpenPipe(path);
+                if (pipeStream != null)
                 {
-                    ushort? fileID = state.AddOpenedFile(path);
+                    ushort? fileID = state.AddOpenedFile(path, pipeStream);
                     if (!fileID.HasValue)
                     {
                         header.Status = NTStatus.STATUS_TOO_MANY_OPENED_FILES;

+ 12 - 20
SMBLibrary/Server/SMB1/ReadWriteResponseHelper.cs

@@ -72,16 +72,20 @@ namespace SMBLibrary.Server.SMB1
                 return null;
             }
             string openedFilePath = openedFile.Path;
-            
+            Stream stream = openedFile.Stream;
             if (share is NamedPipeShare)
             {
-                return state.RetrieveNamedPipeReply(FID);
+                byte[] data = new byte[maxCount];
+                int bytesRead = stream.Read(data, 0, maxCount);
+                if (bytesRead < maxCount)
+                {
+                    // EOF, we must trim the response data array
+                    data = ByteReader.ReadBytes(data, 0, bytesRead);
+                }
+                return data;
             }
             else // FileSystemShare
             {
-                FileSystemShare fileSystemShare = (FileSystemShare)share;
-                IFileSystem fileSystem = fileSystemShare.FileSystem;
-                Stream stream = openedFile.Stream;
 
                 if (stream == null)
                 {
@@ -176,26 +180,14 @@ namespace SMBLibrary.Server.SMB1
                 return 0;
             }
             string openedFilePath = openedFile.Path;
-
+            Stream stream = openedFile.Stream;
             if (share is NamedPipeShare)
             {
-                RemoteService service = ((NamedPipeShare)share).GetService(openedFilePath);
-                if (service != null)
-                {
-                    RPCPDU rpcRequest = RPCPDU.GetPDU(data, 0);
-                    RPCPDU rpcReply = RemoteServiceHelper.GetRPCReply(rpcRequest, service);
-                    byte[] replyData = rpcReply.GetBytes();
-                    state.StoreNamedPipeReply(FID, replyData);
-                    return (uint)data.Length;
-                }
-
-                // This code should not execute unless the SMB request (sequence) is invalid
-                header.Status = NTStatus.STATUS_INVALID_SMB;
-                return 0;
+                stream.Write(data, 0, data.Length);
+                return (uint)data.Length;
             }
             else // FileSystemShare
             {
-                Stream stream = openedFile.Stream;
                 if (stream == null)
                 {
                      header.Status = NTStatus.STATUS_ACCESS_DENIED;

+ 6 - 15
SMBLibrary/Server/SMB1/TransactionSubcommandHelper.cs

@@ -1,4 +1,4 @@
-/* Copyright (C) 2014-2017 Tal Aloni <tal.aloni.il@gmail.com>. All rights reserved.
+/* Copyright (C) 2014 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,
@@ -18,26 +18,17 @@ namespace SMBLibrary.Server.SMB1
     {
         internal static TransactionTransactNamedPipeResponse GetSubcommandResponse(SMB1Header header, TransactionTransactNamedPipeRequest subcommand, NamedPipeShare share, SMB1ConnectionState state)
         {
-            string openedFilePath = state.GetOpenedFilePath(subcommand.FID);
-            if (openedFilePath == null)
+            OpenedFileObject openedFile = state.GetOpenedFileObject(subcommand.FID);
+            if (openedFile == null)
             {
                 header.Status = NTStatus.STATUS_INVALID_HANDLE;
                 return null;
             }
 
             TransactionTransactNamedPipeResponse response = new TransactionTransactNamedPipeResponse();
-            RemoteService service = share.GetService(openedFilePath);
-            if (service != null)
-            {
-                RPCPDU rpcRequest = RPCPDU.GetPDU(subcommand.WriteData, 0);
-                RPCPDU rpcReply = RemoteServiceHelper.GetRPCReply(rpcRequest, service);
-                response.ReadData = rpcReply.GetBytes();
-                return response;
-            }
-
-            // This code should not execute unless the request sequence is invalid
-            header.Status = NTStatus.STATUS_INVALID_SMB;
-            return null;
+            openedFile.Stream.Write(subcommand.WriteData, 0, subcommand.WriteData.Length);
+            response.ReadData = ByteReader.ReadAllBytes(openedFile.Stream);
+            return response;
         }
     }
 }

+ 16 - 2
SMBLibrary/Server/Shares/NamedPipeShare.cs

@@ -6,7 +6,7 @@
  */
 using System;
 using System.Collections.Generic;
-using System.Text;
+using System.IO;
 using SMBLibrary.RPC;
 using SMBLibrary.Services;
 
@@ -23,7 +23,21 @@ namespace SMBLibrary.Server
             this.Add(new WorkstationService(Environment.MachineName, Environment.MachineName));
         }
 
-        public RemoteService GetService(string path)
+        public Stream OpenPipe(string path)
+        {
+            // It is possible to have a named pipe that does not use RPC (e.g. MS-WSP),
+            // However this is not currently needed by our implementation.
+            RemoteService service = GetService(path);
+            if (service != null)
+            {
+                // All instances of a named pipe share the same pipe name, but each instance has its own buffers and handles,
+                // and provides a separate conduit for client/server communication.
+                return new RPCPipeStream(service);
+            }
+            return null;
+        }
+
+        private RemoteService GetService(string path)
         {
             foreach (RemoteService service in this)
             {

+ 116 - 0
SMBLibrary/Services/RPCPipeStream.cs

@@ -0,0 +1,116 @@
+/* Copyright (C) 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 SMBLibrary.RPC;
+
+namespace SMBLibrary.Services
+{
+    public class RPCPipeStream : Stream
+    {
+        private RemoteService m_service;
+        private MemoryStream m_outputStream;
+
+        public RPCPipeStream(RemoteService service)
+        {
+            m_service = service;
+            m_outputStream = new MemoryStream();
+        }
+
+        public override int Read(byte[] buffer, int offset, int count)
+        {
+            return m_outputStream.Read(buffer, offset, count);
+        }
+
+        public override void Write(byte[] buffer, int offset, int count)
+        {
+            int lengthOfPDUs = 0;
+            do
+            {
+                RPCPDU rpcRequest = RPCPDU.GetPDU(buffer, offset);
+                lengthOfPDUs += rpcRequest.FragmentLength;
+                RPCPDU rpcReply = RemoteServiceHelper.GetRPCReply(rpcRequest, m_service);
+                byte[] replyData = rpcReply.GetBytes();
+                Append(replyData);
+            }
+            while (lengthOfPDUs < count);
+        }
+
+        private void Append(byte[] buffer)
+        {
+            long position = m_outputStream.Position;
+            m_outputStream.Position = m_outputStream.Length;
+            m_outputStream.Write(buffer, 0, buffer.Length);
+            m_outputStream.Seek(position, SeekOrigin.Begin);
+        }
+
+        public override void Flush()
+        {
+        }
+
+        public override void Close()
+        {
+            m_outputStream.Close();
+        }
+
+        public override long Seek(long offset, SeekOrigin origin)
+        {
+            throw new NotSupportedException();
+        }
+
+        public override void SetLength(long value)
+        {
+            throw new NotSupportedException();
+        }
+
+        public override bool CanSeek
+        {
+            get
+            {
+                return false;
+            }
+        }
+
+        public override bool CanRead
+        {
+            get
+            {
+                return m_outputStream.CanRead;
+            }
+        }
+
+        public override bool CanWrite
+        {
+            get
+            {
+                return m_outputStream.CanWrite;
+            }
+        }
+
+        public override long Length
+        {
+            get
+            {
+                // Stream.Length only works on Stream implementations where seeking is available.
+                throw new NotSupportedException();
+            }
+        }
+
+        public override long Position
+        {
+            get
+            {
+                throw new NotSupportedException();
+            }
+            set
+            {
+                throw new NotSupportedException();
+            }
+        }
+    }
+}