Ejemplo n.º 1
0
        /// <summary>
        /// Does some basic authentication checks and then
        /// dispatches to the JayRock RPC system.
        /// </summary>
        /// <param name="message">The JSON-RPC formatted message.</param>
        /// <param name="keePassRPCClient">The client we're communicating with.</param>
        private void DispatchToRPCService(string message, KeePassRPCClientConnection keePassRPCClientConnection)
        {
            if (KeePassRPCPlugin.logger != null)
            {
                KeePassRPCPlugin.logger.WriteLine("Preparing to dispatch: " + message);
            }
            StringBuilder sb = new StringBuilder();
            string        requiredResultRegex    = "";
            long          authorisationAttemptId = -1;

            if (!keePassRPCClientConnection.Authorised && _authorisationRequired)
            {
                Match match = Regex.Match(message,
                                          "^\\{.*?\\\"method\\\"\\:\\\"Idle\\\".*?,.*?\\\"id\\\"\\:(\\d+).*?\\}$");
                if (match.Success)
                {
                    if (KeePassRPCPlugin.logger != null)
                    {
                        KeePassRPCPlugin.logger.WriteLine("Got Idle method- ignoring.");
                    }
                    // Do nothing
                    return;
                }
                else   // Check for alternative Idle message structure
                {
                    match = Regex.Match(message,
                                        "^\\{.*?\\\"id\\\"\\:(\\d+).*?,.*?\\\"method\\\"\\:\\\"Idle\\\".*?\\}$");
                    if (match.Success)
                    {
                        if (KeePassRPCPlugin.logger != null)
                        {
                            KeePassRPCPlugin.logger.WriteLine("Got Idle method- ignoring.");
                        }
                        // Do nothing
                        return;
                    }
                }
            }

            if (!keePassRPCClientConnection.Authorised && _authorisationRequired)
            {
                // We only accept one type of request if the client has not
                // already authenticated. Maybe it's not nice having to do this
                // outside of the main JayRockJsonRpc library but it'll be good enough

                Match match = Regex.Match(message,
                                          "^\\{.*?\\\"method\\\"\\:\\\"Authenticate\\\".*?,.*?\\\"id\\\"\\:(\\d+).*?\\}$");
                if (!match.Success)
                {
                    match = Regex.Match(message,
                                        "^\\{.*?\\\"id\\\"\\:(\\d+).*?,.*?\\\"method\\\"\\:\\\"Authenticate\\\".*?\\}$");
                    if (!match.Success)
                    {
                        throw new AuthorisationException("Authentication required. You must send a properly formed JSON Authenticate request before using this connection.", -1, 1);
                    }
                }

                authorisationAttemptId = int.Parse(match.Groups[1].Value);
                requiredResultRegex    = "^\\{\\\"id\\\"\\:" + authorisationAttemptId + ",\\\"result\\\"\\:\\{\\\"result\\\"\\:0,\\\"name\\\"\\:\\\"(.*)\\\"\\}\\}$";
            }

            //Stream clientStream = keePassRPCClientConnection.ConnectionStream;

            //TODO2: is this Jayrock stuff thread-safe or do I need new instances of the Service each time?
            JsonRpcDispatcher dispatcher = JsonRpcDispatcherFactory.CreateDispatcher(Service);

            dispatcher.Process(new StringReader(message),
                               new StringWriter(sb), keePassRPCClientConnection.Authorised);
            string output = sb.ToString();

            //MessageBox.Show("result: " + output);
            if (_authorisationRequired && !keePassRPCClientConnection.Authorised)
            {
                string authenticatedClientName;

                // Process the output from the JsonRpcDispatcher which
                // should tell us if the authorisation was successful
                Match match = Regex.Match(output, requiredResultRegex);
                if (match.Success)
                {
                    authenticatedClientName = match.Groups[1].Value;
                    keePassRPCClientConnection.Authorised = true;
                    KeePassRPCPlugin.PromoteNullRPCClient(keePassRPCClientConnection, authenticatedClientName);
                }
                else
                {
                    // If the result follows an accepted syntax we will send
                    // it back to the client so they know why it failed but otherwise...
                    if (!Regex.IsMatch(output,
                                       "^\\{\\\"id\\\"\\:(\\d+),\\\"result\\\"\\:\\{\\\"result\\\"\\:(\\d+),\\\"name\\\"\\:\\\".*\\\"\\}\\}$"))
                    {
                        MessageBox.Show("ERROR! Please click on this box, press CTRL-C on your keyboard and paste into a new post on the KeeFox forum (http://keefox.org/help/forum). Doing this will help other people to use KeeFox without any unexpected error messages like this. Please briefly describe what you were doing when the problem occurred, which version of KeeFox, KeePass and Firefox you use and what other security software you run on your machine. Thanks! Technical detail follows: " + output);
                        return; // maybe could return a proper result indicating failure
                        //but user might get annoyed with this popup appearing every 10 seconds!
                    }
                }
            }

            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(output);
            keePassRPCClientConnection.ConnectionStreamWrite(bytes);
        }
Ejemplo n.º 2
0
        /// <summary>
        /// Does some basic authentication checks and then
        /// dispatches to the JayRock RPC system.
        /// </summary>
        /// <param name="message">The JSON-RPC formatted message.</param>
        /// <param name="keePassRPCClient">The client we're communicating with.</param>
        private void DispatchToRPCService(string message, KeePassRPCClientConnection keePassRPCClientConnection)
        {
            if (KeePassRPCPlugin.logger != null) KeePassRPCPlugin.logger.WriteLine("Preparing to dispatch: " + message);
            StringBuilder sb = new StringBuilder();
            string requiredResultRegex = "";
            long authorisationAttemptId = -1;

            if (!keePassRPCClientConnection.Authorised && _authorisationRequired)
            {
                // We only accept one type of request if the client has not
                // already authenticated. Maybe it's not nice having to do this
                // outside of the main JayRockJsonRpc library but it'll be good enough

                Match match = Regex.Match(message,
                    "^\\{.*?\\\"method\\\"\\:\\\"Authenticate\\\".*?,.*?\\\"id\\\"\\:(\\d+).*?\\}$");
                if (!match.Success)
                {
                    match = Regex.Match(message,
                    "^\\{.*?\\\"id\\\"\\:(\\d+).*?,.*?\\\"method\\\"\\:\\\"Authenticate\\\".*?\\}$");
                    if (!match.Success)
                        throw new AuthorisationException("Authentication required. You must send a properly formed JSON Authenticate request before using this connection.", -1, 1);
                }

                authorisationAttemptId = int.Parse(match.Groups[1].Value);
                requiredResultRegex = "^\\{\\\"id\\\"\\:" + authorisationAttemptId + ",\\\"result\\\"\\:\\{\\\"result\\\"\\:0,\\\"name\\\"\\:\\\"(.*)\\\"\\}\\}$";
            }

            //Stream clientStream = keePassRPCClientConnection.ConnectionStream;

            //TODO2: is this Jayrock stuff thread-safe or do I need new instances of the Service each time?
            JsonRpcDispatcher dispatcher = JsonRpcDispatcherFactory.CreateDispatcher(Service);

            dispatcher.Process(new StringReader(message),
                new StringWriter(sb), keePassRPCClientConnection.Authorised);
            string output = sb.ToString();
            //MessageBox.Show("result: " + output);
            if (_authorisationRequired && !keePassRPCClientConnection.Authorised)
            {
                string authenticatedClientName;

                // Process the output from the JsonRpcDispatcher which
                // should tell us if the authorisation was successful
                Match match = Regex.Match(output, requiredResultRegex);
                if (match.Success)
                {
                    authenticatedClientName = match.Groups[1].Value;
                    keePassRPCClientConnection.Authorised = true;
                    KeePassRPCPlugin.PromoteNullRPCClient(keePassRPCClientConnection, authenticatedClientName);
                }
                else
                {
                    // If the result follows an accepted syntax we will send
                    // it back to the client so they know why it failed but otherwise...
                    if (!Regex.IsMatch(output,
                        "^\\{\\\"id\\\"\\:(\\d+),\\\"result\\\"\\:\\{\\\"result\\\"\\:(\\d+),\\\"name\\\"\\:\\\".*\\\"\\}\\}$"))
                    {
                        MessageBox.Show("ERROR! Please click on this box, press CTRL-C on your keyboard and paste into a new post on the KeeFox forum (http://keefox.org/help/forum). Doing this will help other people to use KeeFox without any unexpected error messages like this. Please briefly describe what you were doing when the problem occurred, which version of KeeFox, KeePass and Firefox you use and what other security software you run on your machine. Thanks! Technical detail follows: " + output);
                        return; // maybe could return a proper result indicating failure
                        //but user might get annoyed with this popup appearing every 10 seconds!
                    }
                }
            }

            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(output);
            keePassRPCClientConnection.ConnectionStreamWrite(bytes);
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Handles the client communication
        /// </summary>
        /// <param name="client">The client.</param>
        private void HandleClientComm(object client)
        {
            KeePassRPCClientConnection keePassRPCClient = null;
            TcpClient     tcpClient    = null;
            NetworkStream clientStream = null;
            SslStream     sslStream    = null;

            if (KeePassRPCPlugin.logger != null)
            {
                KeePassRPCPlugin.logger.WriteLine("HandleClientComm: ");
            }

            if (_useSSL)
            {
                // A client has connected. Create the
                // SslStream using the client's network stream.
                ServicePointManager.ServerCertificateValidationCallback = delegate { return(true); };
                sslStream = new SslStream(((TcpClient)client).GetStream(), false);//, new RemoteCertificateValidationCallback (ValidateServerCertificate),
                //    new LocalCertificateSelectionCallback(SelectLocalCertificate)
                //  );
            }
            else
            {
                tcpClient    = (TcpClient)client;
                clientStream = tcpClient.GetStream();
            }

            if (KeePassRPCPlugin.logger != null)
            {
                KeePassRPCPlugin.logger.WriteLine("stream ready to be authenticated");
            }
            try
            {
                if (_useSSL)
                {
                    // Authenticate the server but don't require the client to
                    // authenticate - we've got our own authentication requirements
                    sslStream.AuthenticateAsServer(
                        _serverCertificate, false, SslProtocols.Ssl3 | SslProtocols.Tls, false);
                    if (KeePassRPCPlugin.logger != null)
                    {
                        KeePassRPCPlugin.logger.WriteLine("stream authenticated");
                    }
                    sslStream.ReadTimeout  = -1;
                    sslStream.WriteTimeout = -1;
                }
                else
                {
                    clientStream.ReadTimeout  = -1;
                    clientStream.WriteTimeout = -1;
                }

                byte[] message = new byte[4096];
                int    bytesRead;
                //bool authorised = false;

                //TODO2: creation of this client probably should happen later
                // but we need to know that this connection needs to be closed
                // during shutdown, even if the client never
                // successfully authenticates.
                keePassRPCClient = _useSSL ? new KeePassRPCClientConnection(sslStream, false) : new KeePassRPCClientConnection(tcpClient, false);
                KeePassRPCPlugin.AddRPCClientConnection(keePassRPCClient);

                // send an "invitation to authenticate" to the new RPC client
                keePassRPCClient.Signal(KeePassRPC.DataExchangeModel.Signal.PLEASE_AUTHENTICATE, "KPRPCListener");

                int           tokenCurlyCount        = 0;
                int           tokenSquareCount       = 0;
                int           adjacentBackslashCount = 0;
                bool          parsingStringContents  = false;
                StringBuilder currentJSONPacket      = new StringBuilder(50);

                // Keep reading data from the network stream whenever it's available
                while (true)
                {
                    bytesRead = 0;

                    try
                    {
                        //blocks until a client sends a message
                        bytesRead = _useSSL ? sslStream.Read(message, 0, 4096) : clientStream.Read(message, 0, 4096);
                    }
                    catch (Exception ex)
                    {
                        if (KeePassRPCPlugin.logger != null)
                        {
                            KeePassRPCPlugin.logger.WriteLine("a socket error has occured:" + ex.ToString());
                        }
                        break;
                    }

                    if (bytesRead == 0)
                    {
                        //the client has disconnected from the server
                        break;
                    }

                    // Can we ever receive a partial UTF8 character? if so, this could go wrong, albeit rarely
                    string receivedData         = System.Text.Encoding.UTF8.GetString(message, 0, bytesRead);
                    int    jsonPacketStartIndex = 0;

                    for (int i = 0; i < receivedData.Length; i++)
                    {
                        bool incrementAdjacentBackslashCount = false;

                        // Use the simple structure of JSON-RPC to extract
                        // complete messages from the network stream
                        switch (receivedData[i])
                        {
                        case TOKEN_QUOT: if (adjacentBackslashCount % 2 == 0)
                            {
                                parsingStringContents = parsingStringContents ? false : true;
                            }
                            break;

                        case TOKEN_BACKSLASH: incrementAdjacentBackslashCount = true;
                            break;

                        case TOKEN_CURLY_START: if (!parsingStringContents)
                            {
                                tokenCurlyCount++;
                            }
                            break;

                        case TOKEN_CURLY_END: if (!parsingStringContents)
                            {
                                tokenCurlyCount--;
                            }
                            break;

                        case TOKEN_SQUARE_START: if (!parsingStringContents)
                            {
                                tokenSquareCount++;
                            }
                            break;

                        case TOKEN_SQUARE_END: if (!parsingStringContents)
                            {
                                tokenSquareCount--;
                            }
                            break;
                        }

                        if (incrementAdjacentBackslashCount)
                        {
                            adjacentBackslashCount++;
                        }
                        else
                        {
                            adjacentBackslashCount = 0;
                        }

                        // When both counts are zero, we know we have
                        // reached the end of a JSON-RPC request
                        if (tokenCurlyCount == 0 && tokenSquareCount == 0)
                        {
                            currentJSONPacket.Append(receivedData.Substring(
                                                         jsonPacketStartIndex, i - jsonPacketStartIndex + 1));
                            DispatchToRPCService(currentJSONPacket.ToString(), keePassRPCClient);
                            currentJSONPacket    = new StringBuilder(50);
                            jsonPacketStartIndex = i + 1;
                        }
                    }

                    // If the JSON request is not complete we store what we have already found
                    if (tokenCurlyCount != 0 || tokenSquareCount != 0)
                    {
                        currentJSONPacket.Append(receivedData);
                    }
                    // http://groups.google.com/group/jayrock/browse_thread/thread/59cf6a58bc63f0df/a7775c3097cf6957?lnk=gst&q=thread+JsonRpcDispatcher+#a7775c3097cf6957
                }
            }
            catch (AuthorisationException authEx)
            {
                // Send a JSON message down the pipe
                byte[] bytes = System.Text.Encoding.UTF8.GetBytes(authEx.AsJSONResult());
                keePassRPCClient.ConnectionStreamWrite(bytes);
            }
            catch (AuthenticationException ex)
            {
                if (KeePassRPCPlugin.logger != null)
                {
                    KeePassRPCPlugin.logger.WriteLine("Authentication exception: " + ex.ToString());
                }
                // Nothing we can do about this since client can't
                // receive messages over an invalid network stream
            }
            catch (Exception e)
            {
                if (KeePassRPCPlugin.logger != null)
                {
                    KeePassRPCPlugin.logger.WriteLine("Unknown exception: " + e.ToString());
                }
                //TODO2: send a JSON message down the pipe
                // ex.AsJSONResult();
            }
            finally
            {
                if (KeePassRPCPlugin.logger != null)
                {
                    KeePassRPCPlugin.logger.WriteLine("!!!Hit finally");
                }
                if (keePassRPCClient != null)
                {
                    KeePassRPCPlugin.RemoveRPCClientConnection(keePassRPCClient);
                }
                if (_useSSL)
                {
                    try {
                        sslStream.Close();
                    }
                    catch (IOException ioex)
                    {
                        // This is okay
                    }
                }
                else
                {
                    clientStream.Close();
                }
            }
        }