Example #1
0
        /// <summary>
        /// Constructs an error result.
        /// </summary>
        /// <param name="request">Specifies the profile request or <c>null</c> when this isn't relevant.</param>
        /// <param name="status">One of the <see cref="ProfileStatus"/> codes.</param>
        /// <param name="message">The error message.</param>
        /// <returns>The <see cref="ProfileHandlerResult"/>.</returns>
        /// <remarks>
        /// <note>
        /// This method will examine the <paramref name="message"/>, looking for
        /// underlying 1Password errors and will potentially override the
        /// <paramref name="status"/> passed.
        /// </note>
        /// </remarks>
        public static ProfileHandlerResult CreateError(ProfileRequest request, string status, string message)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(status), nameof(status));
            Covenant.Requires <ArgumentException>(status != ProfileStatus.OK, nameof(status));
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(message));

            // $hack(jefflill): This is fragile.

            if (message.Contains("503"))
            {
                status = ProfileStatus.OnePasswordUnavailable;
            }

            // Add information about the value being requested to the message.

            if (request != null)
            {
                // $hack(jefflill):
                //
                // Replace any potential secret values with "***" to avoid having these
                // end up in test or other logs.

                const string redacted = "***";

                if (request.Args.ContainsKey("pat"))
                {
                    request.Args["pat"] = redacted;
                }

                if (request.Args.ContainsKey("password"))
                {
                    request.Args["password"] = redacted;
                }

                message = $"[{request}]: {message}";
            }

            return(new ProfileHandlerResult()
            {
                Status = status,
                Error = message
            });
        }
Example #2
0
        /// <summary>
        /// Parses a request from a line of text read from the named pipe.
        /// </summary>
        /// <param name="commandLine">The command line.</param>
        /// <returns>The <see cref="ProfileRequest"/>.</returns>
        /// <exception cref="FormatException">Thrown for invalid command lines.</exception>
        public static ProfileRequest Parse(string commandLine)
        {
            Covenant.Requires <ArgumentNullException>(!string.IsNullOrEmpty(commandLine), nameof(commandLine));

            var colonPos = commandLine.IndexOf(':');

            if (colonPos == -1)
            {
                throw new FormatException("Invalid profile service command line: Command colon is missing.");
            }

            var command = commandLine.Substring(0, colonPos).Trim();

            if (command == string.Empty)
            {
                throw new FormatException("Invalid profile service command line: Command is empty.");
            }

            var request = new ProfileRequest()
            {
                Command = command
            };
            var args = commandLine.Substring(colonPos + 1).Split(commaArray, StringSplitOptions.RemoveEmptyEntries);

            foreach (var arg in args)
            {
                var fields = arg.Split('=');

                if (fields.Length != 2)
                {
                    throw new FormatException("Invalid profile service command line: Malformed argument");
                }

                request.Args[fields[0].Trim()] = fields[1].Trim();
            }

            return(request);
        }
Example #3
0
        /// <summary>
        /// Handles incoming client connections on a background thread.
        /// </summary>
        /// <param name="pipeIndexObject">Passes as the index into the [pipes] array this thread will use for its server side pipe.</param>
        private void ServerThread(object pipeIndexObject)
        {
            try
            {
                var pipeIndex = (int)pipeIndexObject;

                while (true)
                {
                    lock (syncLock)
                    {
                        if (disposing)
                        {
                            // The server is shutting down, so exit the thread.

                            return;
                        }

                        if (pipes[pipeIndex] != null)
                        {
                            pipes[pipeIndex].Dispose();
                            pipes[pipeIndex] = null;
                        }

#pragma warning disable CA1416
                        pipes[pipeIndex] = new NamedPipeServerStream(pipeName, PipeDirection.InOut, maxNumberOfServerInstances: threads.Length, PipeTransmissionMode.Message, PipeOptions.CurrentUserOnly);
#pragma warning restore CA1416
                    }

                    var pipe = pipes[pipeIndex];

                    pipe.WaitForConnection();

                    if (disposing)
                    {
                        return;
                    }

                    var reader = new StreamReader(pipe);
                    var writer = new StreamWriter(pipe);

                    writer.AutoFlush = true;

                    var requestLine   = reader.ReadLine();
                    var request       = (ProfileRequest)null;
                    var handlerResult = (ProfileHandlerResult)null;

                    try
                    {
                        request = ProfileRequest.Parse(requestLine);
                    }
                    catch (FormatException)
                    {
                        // Report an malformed request to the client and then continue
                        // listening for the next request.

                        writer.WriteLine(ProfileResponse.CreateError(ProfileStatus.BadRequest, "Malformed request"));
                        return;
                    }

                    if (GetIsReady != null)
                    {
                        handlerResult = GetIsReady();

                        if (handlerResult != null)
                        {
                            writer.WriteLine(handlerResult.ToResponse());
#pragma warning disable CA1416
                            pipe.WaitForPipeDrain();
#pragma warning restore CA1416
                            pipe.Dispose();
                            pipes[pipeIndex] = null;
                            continue;
                        }
                    }

                    request.Args.TryGetValue("name", out var name);
                    request.Args.TryGetValue("vault", out var vault);
                    request.Args.TryGetValue("masterpassword", out var masterPassword);

                    try
                    {
                        switch (request.Command)
                        {
                        case "GET-PROFILE-VALUE":

                            if (name == null)
                            {
                                handlerResult = ProfileHandlerResult.CreateError(request, ProfileStatus.MissingArg, $"GET-PROFILE-VALUE: [name] argument is required.");
                                break;
                            }

                            handlerResult = GetProfileValueHandler(request, name);
                            break;

                        case "GET-SECRET-PASSWORD":

                            if (name == null)
                            {
                                handlerResult = ProfileHandlerResult.CreateError(request, ProfileStatus.MissingArg, $"GET-SECRET-PASSWORD: [name] argument is required.");
                                break;
                            }

                            handlerResult = GetSecretPasswordHandler(request, name, vault, masterPassword);
                            break;

                        case "GET-SECRET-VALUE":

                            if (name == null)
                            {
                                handlerResult = ProfileHandlerResult.CreateError(request, ProfileStatus.MissingArg, $"GET-SECRET-VALUE: [name] argument is required.");
                                break;
                            }

                            handlerResult = GetSecretValueHandler(request, name, vault, masterPassword);
                            break;

                        case "CALL":

                            if (CallHandler == null)
                            {
                                handlerResult = ProfileHandlerResult.CreateError(request, ProfileStatus.BadCommand, $"Server has no call handler.");
                            }
                            else
                            {
                                handlerResult = CallHandler(request);
                            }
                            break;

                        default:

                            handlerResult = ProfileHandlerResult.CreateError(request, ProfileStatus.BadCommand, $"Unexpected command: {request.Command}");
                            break;
                        }
                    }
                    catch (Exception e)
                    {
                        handlerResult = ProfileHandlerResult.CreateError(request, ProfileStatus.BadCommand, NeonHelper.ExceptionError(e));
                    }

                    writer.WriteLine(handlerResult.ToResponse());
#pragma warning disable CA1416
                    pipe.WaitForPipeDrain();
#pragma warning restore CA1416
                    pipe.Disconnect();
                }
            }
            catch
            {
                // Handle all exceptions by exiting the thread.

                return;
            }
        }