Example #1
0
        void OnMessageReceived(byte[] RawMessage)
        {
            try
            {
                string   Message = Encoding.Unicode.GetString(RawMessage);
                XElement xMsg    = XElement.Parse(Message, LoadOptions.PreserveWhitespace);

                if (xMsg.Name.LocalName == "Connect-Terminal")
                {
                    try
                    {
                        string Domain   = xMsg.Attribute("Domain").Value;
                        string UserName = xMsg.Attribute("UserName").Value;
                        string Password = xMsg.Attribute("Password").Value;

                        lock (ProcessLock)
                        {
                            // Remove any processes which have exited so that instead of reattaching we launch a new one...
                            List <string> ToRemove = new List <string>();
                            foreach (var kvp in Processes)
                            {
                                var TheProcess = kvp.Value;
                                if (TheProcess.CommandPrompt == null || !TheProcess.CommandPrompt.IsStillRunning)
                                {
                                    ToRemove.Add(kvp.Key);
                                }
                            }
                            foreach (string Key in ToRemove)
                            {
                                Processes.Remove(Key);
                            }

                            // Next check if we have this process already open, referenced by full username...
                            string FullUserName;
                            if (Domain.Length > 0)
                            {
                                FullUserName = Domain + "\\" + UserName;
                            }
                            else
                            {
                                FullUserName = UserName;
                            }
                            if (Processes.ContainsKey(FullUserName))
                            {
                                CredentialValidation.ValidateUser(Domain, UserName, Password);
                                ConnectedProcess = Processes[FullUserName];

                                if (!ConsoleApi.AttachConsole((uint)ConnectedProcess.CommandPrompt.ProcessId))
                                {
                                    throw new Win32Exception(Marshal.GetLastWin32Error());
                                }

                                if (Conin != null)
                                {
                                    Conin.Dispose(); Conin = null;
                                }
                                Conin = new ConsoleInput();

                                if (CT != null)
                                {
                                    CT.Dispose(); CT = null;
                                }
                                CT = new ConsoleTracker();

                                string XmlMsg = "<Terminal-Connected Reconnected=\"true\" full-user-name=\"" + FullUserName + "\" />";
                                byte[] RawMsg = Encoding.Unicode.GetBytes(XmlMsg);
                                SendMessage(NetworkStream, RawMsg);
                            }
                            else
                            {
                                ConnectedProcess = new SlaveProcess();
                                try
                                {
#if DEBUG && ShowCmdWindow
                                    ConnectedProcess.Start(Domain, UserName, Password, IsLocalProvider);
#else
                                    ConnectedProcess.Start(Domain, UserName, Password, false);
#endif

                                    Processes.Add(FullUserName, ConnectedProcess);

                                    Thread.Sleep(2500);         // TODO: WaitForInputIdle() would be preferred and more robust.

                                    //ConnectedProcess.CommandPrompt.WaitForInputIdle(15000);

                                    if (!ConnectedProcess.CommandPrompt.IsStillRunning)
                                    {
                                        throw new Exception("Newly launched terminal appears to have exited immediately or never actually launched.");
                                    }

                                    if (!ConsoleApi.AttachConsole((uint)ConnectedProcess.CommandPrompt.ProcessId))
                                    {
                                        this.Log.WriteLine("AttachConsole() failed.", Severity.Debug);
                                        throw new Win32Exception(Marshal.GetLastWin32Error());
                                    }

                                    this.Log.WriteLine("Console attached.", Severity.Debug);

                                    if (Conin != null)
                                    {
                                        Conin.Dispose(); Conin = null;
                                    }
                                    Conin = new ConsoleInput();

                                    if (CT != null)
                                    {
                                        CT.Dispose(); CT = null;
                                    }
                                    CT = new ConsoleTracker();

                                    this.Log.WriteLine("Console established without errors.", Severity.Debug);
                                }
                                finally
                                {
                                    if (ConnectedProcess.CommandPrompt.DebugLog.Length > 0)
                                    {
                                        this.Log.WriteLine(ConnectedProcess.CommandPrompt.DebugLog.ToString(), Severity.Debug);
                                        string XmlMsg = "<Debug>" + ConnectedProcess.CommandPrompt.DebugLog.ToString() + "</Debug>";
                                        byte[] RawMsg = Encoding.Unicode.GetBytes(XmlMsg);
                                        SendMessage(NetworkStream, RawMsg);
                                        ConnectedProcess.CommandPrompt.DebugLog.Clear();
                                    }
                                }

                                {
                                    string XmlMsg = "<Terminal-Connected Reconnected=\"false\" full-user-name=\"" + FullUserName + "\" />";
                                    byte[] RawMsg = Encoding.Unicode.GetBytes(XmlMsg);
                                    SendMessage(NetworkStream, RawMsg);
                                }
                            }
                        }
                    }
                    catch (Exception ex)
                    {
                        string EncodedText = System.Convert.ToBase64String(Encoding.Unicode.GetBytes(ex.ToString() + "\n"));
                        string XmlMsg      = "<Error>" + EncodedText + "</Error>";
                        byte[] RawMsg      = Encoding.Unicode.GetBytes(XmlMsg);
                        SendMessage(NetworkStream, RawMsg);
                    }
                }

                if (xMsg.Name.LocalName == "Console-Input")
                {
                    // TODO: This xml format isn't very efficient.  Could at least string it all into one array of INPUT_RECORDs.
                    List <ConsoleApi.KEY_EVENT_RECORD> Keys = new List <ConsoleApi.KEY_EVENT_RECORD>();
                    foreach (var Key in xMsg.Elements())
                    {
                        if (Key.Name != "Key")
                        {
                            throw new FormatException("Expected <Key> objects in <Console-Input>");
                        }
                        ConsoleApi.KEY_EVENT_RECORD KER = new ConsoleApi.KEY_EVENT_RECORD();
                        KER.bKeyDown          = Boolean.Parse(Key.Attribute("down").Value);
                        KER.dwControlKeyState = (ConsoleApi.ControlKeyState)UInt32.Parse(Key.Attribute("ctrl").Value);
                        KER.UnicodeChar       = (char)UInt32.Parse(Key.Attribute("char").Value);
                        KER.wRepeatCount      = UInt16.Parse(Key.Attribute("repeat").Value);
                        KER.wVirtualKeyCode   = UInt16.Parse(Key.Attribute("key").Value);
                        KER.wVirtualScanCode  = UInt16.Parse(Key.Attribute("scan").Value);
                        Keys.Add(KER);
                    }

                    //byte[] Raw = System.Convert.FromBase64String(xMsg.Value);
                    //string Msg = Encoding.Unicode.GetString(Raw);
                    lock (ProcessLock)
                    {
                        if (ConnectedProcess != null)
                        {
                            StringBuilder Text = new StringBuilder();
                            foreach (var Key in Keys)
                            {
                                Text.Append(Key.UnicodeChar);
                            }
                            //Debug.WriteLine("Writing to console as input: '" + Text + "'");

                            ConsoleApi.INPUT_RECORD[] irs = new ConsoleApi.INPUT_RECORD[1];
                            ConsoleApi.INPUT_RECORD   ir  = new ConsoleApi.INPUT_RECORD();
                            ir.EventType = (ushort)ConsoleApi.EventTypes.KEY_EVENT;

                            foreach (var Key in Keys)
                            {
                                ir.KeyEvent = Key;
                                irs[0]      = ir;
                                Conin.Write(irs);
                            }

                            //Thread.Sleep(50);
                            //CT.DebugWholeConsole();
                        }
                    }
                }

                if (xMsg.Name.LocalName == "Reload-Console")
                {
                    lock (ProcessLock) CT.PendingReload = true;
                }
            }
            catch (Exception ex)
            {
                Debug.Write("Exception detected in SlaveCore.OnMessageReceived(): " + ex.ToString());
                throw ex;
            }
        }
Example #2
0
        protected void ClientThreadEntry()
        {
            try
            {
                TcpListener listenerV4 = new TcpListener(IPAddress.Any, PortNumber);
                TcpListener listenerV6 = new TcpListener(IPAddress.IPv6Any, PortNumber);
                listenerV4.Start(10);
                listenerV6.Start(10);

                // Start listening for connections.

                Log.WriteLine("Listening for TCP connections on port " + PortNumber + "...\n", Severity.Debug);
                while (!Stopping)
                {
                    TcpClient ClientRequest;
                    try
                    {
                        if (listenerV4.Pending())
                        {
                            ClientRequest = listenerV4.AcceptTcpClient();
                        }
                        else if (listenerV6.Pending())
                        {
                            ClientRequest = listenerV6.AcceptTcpClient();
                        }
                        else
                        {
                            Thread.Sleep(250); continue;
                        }

                        Log.WriteLine("TCP Client accepted.\n", Severity.Debug);

                        NetworkStream = new SslStream(ClientRequest.GetStream(), false);
                        NetworkStream.AuthenticateAsServer(GetRemoteDesktopCertificate(), false, SslProtocols.Tls12, true);
                        if (!NetworkStream.IsAuthenticated)
                        {
                            NetworkStream = null;
                            Log.WriteLine("Unable to authenticate incoming connection from " + ((IPEndPoint)ClientRequest.Client.RemoteEndPoint).Address.ToString() + ".", Severity.Warning);
                            ClientRequest.Close();
                            continue;
                        }
                        if (!NetworkStream.IsEncrypted)
                        {
                            NetworkStream = null;
                            Log.WriteLine("Unable to encrypt incoming connection from " + ((IPEndPoint)ClientRequest.Client.RemoteEndPoint).Address.ToString() + ".", Severity.Warning);
                            ClientRequest.Close();
                            continue;
                        }
                        // Display the properties and settings for the authenticated stream.
                        Log.WriteLine("Authentication successful [host].\n", Severity.Debug);
#if DEBUG
                        DisplaySecurityLevel(Log, NetworkStream, Severity.Debug);
                        DisplaySecurityServices(Log, NetworkStream, Severity.Debug);
                        DisplayCertificateInformation(Log, NetworkStream, Severity.Debug);
                        DisplayStreamProperties(Log, NetworkStream, Severity.Debug);
#endif

                        // Transmit our version info as a hello.
                        System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
                        FileVersionInfo            fvi      = FileVersionInfo.GetVersionInfo(assembly.Location);
                        string version = fvi.FileVersion;
                        string XmlMsg  = "<Portal-Version Core-Library=\"" + fvi.FileVersion + "\" />";
                        byte[] RawMsg  = Encoding.Unicode.GetBytes(XmlMsg);
                        if (NetworkStream != null)
                        {
                            SendMessage(NetworkStream, RawMsg);
                        }
                    }
                    catch (Win32Exception ex)
                    {
                        Log.WriteLine("Exception accepting new connection: " + ex.ToString(), Severity.Debug);
                        if ((uint)ex.ErrorCode == 0x80004005)
                        {
                            // The credentials supplied to the pacakage were not recognized.
                            // This error comes up rom NetworkStream.AuthenticateAsServer() or GetRemoteDesktopCertificate() for localhost slave when there is no administrative access.
                            Log.WriteLine("This exception usually indicates that administrative access was required but not present.", Severity.Debug);
                        }
                        continue;
                    }
                    catch (Exception ex)
                    {
                        // Note: "The credentials supplied to the package were not recognized" is probably a sign that the code doesn't have Administrator access and can't access the subsystem or certificate it needs.
                        Log.WriteLine("Exception accepting new connection: " + ex.ToString(), Severity.Debug);
                        continue;
                    }

                    try
                    {
                        ServiceConnection(NetworkStream, ClientRequest, ClientRequest.GetStream());
                    }
                    catch (Exception ex)
                    {
                        Log.WriteLine("Exception servicing connection: " + ex.ToString(), Severity.Debug);
                    }

                    lock (ProcessLock)
                    {
                        // Setting ConnectedProcess to null before accepting a new connection is essential to security.  It prevents access to the console without a new authentication and Connect-Terminal.
                        ConnectedProcess = null;

                        // If we aren't attached to a console, FreeConsole() would return ERROR_INVALID_PARAMETER.
                        ConsoleApi.FreeConsole();

                        Log.WriteLine("Closing connection in SlaveCore (if it isn't already).", Severity.Debug);
                        if (ClientRequest != null)
                        {
                            ClientRequest.Dispose(); ClientRequest = null;
                        }

                        if (Conin != null)
                        {
                            Conin.Dispose(); Conin = null;
                        }
                        if (CT != null)
                        {
                            CT.Dispose(); CT = null;
                        }
                    }
                }

                lock (ProcessLock)
                {
                    ConnectedProcess = null;
                    foreach (var kvp in Processes)
                    {
                        kvp.Value.Dispose();
                    }
                    Processes.Clear();
                }
            }
            catch (Exception e)
            {
                Log.WriteLine("Error: " + e.ToString(), Severity.Error);
            }
        }