[Merge] lp:~thomas-voss/trust-store/add-trust-stored into lp:trust-store

Seth Arnold seth.arnold at canonical.com
Sat Aug 2 01:24:21 UTC 2014


Review: Approve

I have a few small comments inline; looks good to me. Thanks!

Diff comments:

> === modified file 'debian/control'
> --- debian/control	2014-07-22 15:57:22 +0000
> +++ debian/control	2014-07-31 13:42:29 +0000
> @@ -11,6 +11,7 @@
>  # in libstdc++ causing us issues, we explicitly select a G++
>  # version.
>                 g++-4.9,
> +               libapparmor-dev,
>                 libboost-program-options-dev,
>                 libboost-system-dev,
>                 libdbus-cpp-dev (>= 4.0.0),
> @@ -60,13 +61,24 @@
>   This package includes all the development headers and libraries for
>   trust-store.
>  
> +Package: trust-store-bin
> +Section: devel
> +Architecture: any
> +Depends: libtrust-store1 (= ${binary:Version}),
> +         ${misc:Depends},
> +Description: Daemon binaries to be used by services.
> + Provides a common implementation of a trust store to be used by trusted
> + helpers.
> + .
> + Daemon binaries to be used by services.
> +
>  Package: trust-store-tests
>  Section: libdevel
>  Architecture: any
>  Depends: libtrust-store1 (= ${binary:Version}),
>           ${misc:Depends},
>  Suggests: libtrust-store-dev,
> -Description: Test files for libtrust-store0
> +Description: Test files for libtrust-store1
>   Provides a common implementation of a trust store to be used by trusted
>   helpers.
>   .
> 
> === modified file 'debian/libtrust-store1.symbols'
> --- debian/libtrust-store1.symbols	2014-07-18 09:41:24 +0000
> +++ debian/libtrust-store1.symbols	2014-07-31 13:42:29 +0000
> @@ -1,4 +1,7 @@
>  libtrust-store.so.1 libtrust-store1 #MINVER#
> + (c++)"core::trust::CachedAgent::Configuration::~Configuration()@Base" 0replaceme
> + (c++)"core::trust::CachedAgent::authenticate_request_with_parameters(core::trust::Agent::RequestParameters const&)@Base" 0replaceme
> + (c++)"core::trust::CachedAgent::CachedAgent(core::trust::CachedAgent::Configuration const&)@Base" 0replaceme
>   (c++)"core::trust::create_default_store(std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.0.1+14.10.20140626.1
>   (c++)"core::trust::process_trust_request(core::trust::RequestParameters const&)@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::expose_store_to_bus_with_name(std::shared_ptr<core::trust::Store> const&, std::shared_ptr<core::dbus::Bus> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.0.1+14.10.20140626.1
> @@ -8,30 +11,68 @@
>   (c++)"core::trust::mir::PromptProviderHelper::InvocationArguments::~InvocationArguments()@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::mir::PromptProviderHelper::exec_prompt_provider_with_arguments(core::trust::mir::PromptProviderHelper::InvocationArguments const&)@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::mir::PromptProviderHelper::PromptProviderHelper(core::trust::mir::PromptProviderHelper::CreationArguments const&)@Base" 0.0.1+14.10.20140717.8
> - (c++)"core::trust::mir::ConnectionVirtualTable::create_prompt_session_sync(int, void (*)(MirPromptSession*, MirPromptSessionState, void*), void*)@Base" 0.0.1+14.10.20140717.8
> + (c++)"core::trust::mir::ConnectionVirtualTable::create_prompt_session_sync(core::trust::TaggedInteger<core::trust::tag::Pid, int>, void (*)(MirPromptSession*, MirPromptSessionState, void*), void*)@Base" 0replaceme
>   (c++)"core::trust::mir::ConnectionVirtualTable::ConnectionVirtualTable(MirConnection*)@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::mir::PromptSessionVirtualTable::release_sync()@Base" 0.0.1+14.10.20140717.8
> - (c++|arch=amd64 armhf64 ppc64el)"core::trust::mir::PromptSessionVirtualTable::mir_client_fd_callback(MirPromptSession*, unsigned long, int const*, void*)@Base" 0.0.1+14.10.20140717.8
> - (c++|arch=armhf i386 powerpc)"core::trust::mir::PromptSessionVirtualTable::mir_client_fd_callback(MirPromptSession*, unsigned int, int const*, void*)@Base" 0.0.1+14.10.20140717.8
> + (c++|arch=amd64 arm64 ppc64el)"core::trust::mir::PromptSessionVirtualTable::mir_client_fd_callback(MirPromptSession*, unsigned long, int const*, void*)@Base" 0replaceme
> + (c++|arch=i386 armhf powerpc)"core::trust::mir::PromptSessionVirtualTable::mir_client_fd_callback(MirPromptSession*, unsigned int, int const*, void*)@Base" 0replaceme
>   (c++)"core::trust::mir::PromptSessionVirtualTable::new_fd_for_prompt_provider()@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::mir::PromptSessionVirtualTable::PromptSessionVirtualTable(MirPromptSession*)@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::mir::create_agent_for_mir_connection(MirConnection*)@Base" 0.0.1+14.10.20140717.8
> - (c++)"core::trust::mir::Agent::prompt_user_for_request(int, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::mir::Agent::on_trust_session_changed_state(MirPromptSession*, MirPromptSessionState, void*)@Base" 0.0.1+14.10.20140717.8
> + (c++)"core::trust::mir::Agent::authenticate_request_with_parameters(core::trust::Agent::RequestParameters const&)@Base" 0replaceme
>   (c++)"core::trust::mir::Agent::translator_only_accepting_exit_status_success()@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::mir::Agent::Agent(std::shared_ptr<core::trust::mir::ConnectionVirtualTable> const&, std::shared_ptr<core::trust::mir::PromptProviderHelper> const&, std::function<core::trust::Request::Answer (core::posix::wait::Result const&)> const&)@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::mir::Agent::~Agent()@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::mir::operator==(core::trust::mir::PromptProviderHelper::InvocationArguments const&, core::trust::mir::PromptProviderHelper::InvocationArguments const&)@Base" 0.0.1+14.10.20140717.8
> + (c++)"core::trust::dbus::create_per_user_agent_for_bus_connection(std::shared_ptr<core::dbus::Bus> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0replaceme
> + (c++)"core::trust::dbus::create_multi_user_agent_for_bus_connection(std::shared_ptr<core::dbus::Bus> const&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)@Base" 0replaceme
> + (c++)"core::trust::Agent::RequestParameters::~RequestParameters()@Base" 0replaceme
>   (c++)"core::trust::Store::Query::Errors::NoCurrentResult::~NoCurrentResult()@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::Store::Query::Errors::QueryIsInErrorState::~QueryIsInErrorState()@Base" 0.0.1+14.10.20140717.8
>   (c++)"core::trust::Store::Errors::ErrorResettingStore::~ErrorResettingStore()@Base" 0.0.1+14.10.20140626.1
> + (c++)"core::trust::remote::dbus::Agent::Stub::send(core::trust::Agent::RequestParameters const&)@Base" 0replaceme
> + (c++)"core::trust::remote::dbus::Agent::Stub::Stub(core::trust::remote::dbus::Agent::Stub::Configuration const&)@Base" 0replaceme
> + (c++)"core::trust::remote::dbus::Agent::Stub::~Stub()@Base" 0replaceme
> + (c++)"core::trust::remote::dbus::Agent::Skeleton::authenticate_request_with_parameters(core::trust::Agent::RequestParameters const&)@Base" 0replaceme
> + (c++)"core::trust::remote::dbus::Agent::Skeleton::Skeleton(core::trust::remote::dbus::Agent::Skeleton::Configuration const&)@Base" 0replaceme
> + (c++)"core::trust::remote::dbus::Agent::Skeleton::~Skeleton()@Base" 0replaceme
> + (c++)"core::trust::remote::Agent::Stub::authenticate_request_with_parameters(core::trust::Agent::RequestParameters const&)@Base" 0replaceme
> + (c++)"core::trust::remote::Agent::Skeleton::authenticate_request_with_parameters(core::trust::Agent::RequestParameters const&)@Base" 0replaceme
> + (c++)"core::trust::remote::Agent::Skeleton::Skeleton(std::shared_ptr<core::trust::Agent> const&)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::start_accept()@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::on_new_session(boost::system::error_code const&, std::shared_ptr<core::trust::remote::posix::Stub::Session> const&)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::create_stub_for_configuration(core::trust::remote::posix::Stub::Configuration const&)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::get_sock_opt_credentials_resolver()@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::handle_error_from_socket_operation_for_uid(boost::system::error_code const&, core::trust::TaggedInteger<core::trust::tag::Uid, unsigned int>)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::send(core::trust::Agent::RequestParameters const&)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::Session::Registry::add_session_for_uid(core::trust::TaggedInteger<core::trust::tag::Uid, unsigned int>, std::shared_ptr<core::trust::remote::posix::Stub::Session>)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::Session::Registry::remove_session_for_uid(core::trust::TaggedInteger<core::trust::tag::Uid, unsigned int>)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::Session::Registry::resolve_session_for_uid(core::trust::TaggedInteger<core::trust::tag::Uid, unsigned int>)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::Session::Session(boost::asio::io_service&)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::Stub(core::trust::remote::posix::Stub::Configuration)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::~Stub()@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Skeleton::start_read()@Base" 0replaceme
> + (c++|arch=amd64 arm64 ppc64el)"core::trust::remote::posix::Skeleton::on_read_finished(boost::system::error_code const&, unsigned long)@Base" 0replaceme
> + (c++|arch=i386 armhf powerpc)"core::trust::remote::posix::Skeleton::on_read_finished(boost::system::error_code const&, unsigned int)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Skeleton::process_incoming_request(core::trust::remote::posix::Request const&)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Skeleton::create_skeleton_for_configuration(core::trust::remote::posix::Skeleton::Configuration const&)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Skeleton::Skeleton(core::trust::remote::posix::Skeleton::Configuration const&)@Base" 0replaceme
> + (c++)"core::trust::remote::posix::Skeleton::~Skeleton()@Base" 0replaceme
> + (c++)"core::trust::remote::helpers::proc_stat_start_time_resolver()@Base" 0replaceme
> + (c++)"core::trust::remote::helpers::aa_get_task_con_app_id_resolver()@Base" 0replaceme
> + (c++)"core::trust::operator==(core::trust::Agent::RequestParameters const&, core::trust::Agent::RequestParameters const&)@Base" 0replaceme
>   (c++)"core::trust::operator==(core::trust::Request const&, core::trust::Request const&)@Base" 0.0.1+14.10.20140626.1
>   (c++)"core::trust::operator<<(std::basic_ostream<char, std::char_traits<char> >&, core::trust::Request::Answer const&)@Base" 0.0.1+14.10.20140626.1
>   (c++)"core::trust::operator<<(std::basic_ostream<char, std::char_traits<char> >&, core::trust::Request const&)@Base" 0.0.1+14.10.20140626.1
> + (c++)"core::trust::remote::posix::Stub::has_session_for_uid(core::trust::TaggedInteger<core::trust::tag::Uid, unsigned int>) const at Base" 0replaceme
> + (c++)"core::trust::remote::posix::Stub::Session::Registry::has_session_for_uid(core::trust::TaggedInteger<core::trust::tag::Uid, unsigned int>) const at Base" 0replaceme
> + (c++)"typeinfo for core::trust::CachedAgent at Base" 0replaceme
>   (c++)"typeinfo for core::trust::mir::PromptProviderHelper at Base" 0.0.1+14.10.20140717.8
>   (c++)"typeinfo for core::trust::mir::ConnectionVirtualTable at Base" 0.0.1+14.10.20140717.8
>   (c++)"typeinfo for core::trust::mir::PromptSessionVirtualTable at Base" 0.0.1+14.10.20140717.8
>   (c++)"typeinfo for core::trust::mir::Agent at Base" 0.0.1+14.10.20140717.8
> + (c++)"typeinfo for core::trust::Agent::Registry at Base" 0replaceme
>   (c++)"typeinfo for core::trust::Agent at Base" 0.0.1+14.10.20140717.8
>   (c++)"typeinfo for core::trust::Store::Query::Errors::NoCurrentResult at Base" 0.0.1+14.10.20140717.8
>   (c++)"typeinfo for core::trust::Store::Query::Errors::QueryIsInErrorState at Base" 0.0.1+14.10.20140717.8
> @@ -39,10 +80,19 @@
>   (c++)"typeinfo for core::trust::Store::Errors::ErrorResettingStore at Base" 0.0.1+14.10.20140626.1
>   (c++)"typeinfo for core::trust::Store at Base" 0.0.1+14.10.20140626.1
>   (c++)"typeinfo for core::trust::Token at Base" 0.0.1+14.10.20140626.1
> + (c++)"typeinfo for core::trust::remote::dbus::Agent::Stub at Base" 0replaceme
> + (c++)"typeinfo for core::trust::remote::dbus::Agent::Skeleton at Base" 0replaceme
> + (c++)"typeinfo for core::trust::remote::Agent::Stub at Base" 0replaceme
> + (c++)"typeinfo for core::trust::remote::Agent::Skeleton at Base" 0replaceme
> + (c++)"typeinfo for core::trust::remote::posix::Stub::Session::Registry at Base" 0replaceme
> + (c++)"typeinfo for core::trust::remote::posix::Stub at Base" 0replaceme
> + (c++)"typeinfo for core::trust::remote::posix::Skeleton at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::CachedAgent at Base" 0replaceme
>   (c++)"typeinfo name for core::trust::mir::PromptProviderHelper at Base" 0.0.1+14.10.20140717.8
>   (c++)"typeinfo name for core::trust::mir::ConnectionVirtualTable at Base" 0.0.1+14.10.20140717.8
>   (c++)"typeinfo name for core::trust::mir::PromptSessionVirtualTable at Base" 0.0.1+14.10.20140717.8
>   (c++)"typeinfo name for core::trust::mir::Agent at Base" 0.0.1+14.10.20140717.8
> + (c++)"typeinfo name for core::trust::Agent::Registry at Base" 0replaceme
>   (c++)"typeinfo name for core::trust::Agent at Base" 0.0.1+14.10.20140717.8
>   (c++)"typeinfo name for core::trust::Store::Query::Errors::NoCurrentResult at Base" 0.0.1+14.10.20140717.8
>   (c++)"typeinfo name for core::trust::Store::Query::Errors::QueryIsInErrorState at Base" 0.0.1+14.10.20140717.8
> @@ -50,10 +100,19 @@
>   (c++)"typeinfo name for core::trust::Store::Errors::ErrorResettingStore at Base" 0.0.1+14.10.20140626.1
>   (c++)"typeinfo name for core::trust::Store at Base" 0.0.1+14.10.20140626.1
>   (c++)"typeinfo name for core::trust::Token at Base" 0.0.1+14.10.20140626.1
> + (c++)"typeinfo name for core::trust::remote::dbus::Agent::Stub at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::remote::dbus::Agent::Skeleton at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::remote::Agent::Stub at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::remote::Agent::Skeleton at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::remote::posix::Stub::Session::Registry at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::remote::posix::Stub at Base" 0replaceme
> + (c++)"typeinfo name for core::trust::remote::posix::Skeleton at Base" 0replaceme
> + (c++)"vtable for core::trust::CachedAgent at Base" 0replaceme
>   (c++)"vtable for core::trust::mir::PromptProviderHelper at Base" 0.0.1+14.10.20140717.8
>   (c++)"vtable for core::trust::mir::ConnectionVirtualTable at Base" 0.0.1+14.10.20140717.8
>   (c++)"vtable for core::trust::mir::PromptSessionVirtualTable at Base" 0.0.1+14.10.20140717.8
>   (c++)"vtable for core::trust::mir::Agent at Base" 0.0.1+14.10.20140717.8
> + (c++)"vtable for core::trust::Agent::Registry at Base" 0replaceme
>   (c++)"vtable for core::trust::Agent at Base" 0.0.1+14.10.20140717.8
>   (c++)"vtable for core::trust::Store::Query::Errors::NoCurrentResult at Base" 0.0.1+14.10.20140717.8
>   (c++)"vtable for core::trust::Store::Query::Errors::QueryIsInErrorState at Base" 0.0.1+14.10.20140717.8
> @@ -61,3 +120,10 @@
>   (c++)"vtable for core::trust::Store::Errors::ErrorResettingStore at Base" 0.0.1+14.10.20140626.1
>   (c++)"vtable for core::trust::Store at Base" 0.0.1+14.10.20140626.1
>   (c++)"vtable for core::trust::Token at Base" 0.0.1+14.10.20140626.1
> + (c++)"vtable for core::trust::remote::dbus::Agent::Stub at Base" 0replaceme
> + (c++)"vtable for core::trust::remote::dbus::Agent::Skeleton at Base" 0replaceme
> + (c++)"vtable for core::trust::remote::Agent::Stub at Base" 0replaceme
> + (c++)"vtable for core::trust::remote::Agent::Skeleton at Base" 0replaceme
> + (c++)"vtable for core::trust::remote::posix::Stub::Session::Registry at Base" 0replaceme
> + (c++)"vtable for core::trust::remote::posix::Stub at Base" 0replaceme
> + (c++)"vtable for core::trust::remote::posix::Skeleton at Base" 0replaceme
> 
> === added file 'debian/trust-store-bin.install'
> --- debian/trust-store-bin.install	1970-01-01 00:00:00 +0000
> +++ debian/trust-store-bin.install	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,1 @@
> +usr/bin/trust*
> \ No newline at end of file
> 
> === modified file 'include/core/trust/agent.h'
> --- include/core/trust/agent.h	2014-07-16 20:26:03 +0000
> +++ include/core/trust/agent.h	2014-07-31 13:42:29 +0000
> @@ -20,15 +20,15 @@
>  #define CORE_TRUST_AGENT_H_
>  
>  #include <core/trust/request.h>
> +#include <core/trust/tagged_integer.h>
>  #include <core/trust/visibility.h>
>  
> +#include <cstdint>
> +
>  namespace core
>  {
>  namespace trust
>  {
> -// Forward-declarations.
> -struct RequestParameters;
> -
>  /** @brief Abstracts user-prompting functionality. */
>  class CORE_TRUST_DLL_PUBLIC Agent
>  {
> @@ -43,14 +43,52 @@
>      Agent& operator=(Agent&&) = delete;
>      /** @endcond */
>  
> +    /** @brief Abstracts functionality for storing agent instances and associating them with a user id. */
> +    struct Registry
> +    {
> +        /** @brief Convenience typedef for a shared ptr. */
> +        typedef std::shared_ptr<Registry> Ptr;
> +
> +        /** @cond */
> +        Registry() = default;
> +        virtual ~Registry() = default;
> +        /** @endcond */
> +
> +        /** @brief Registers an agent for the given uid. */
> +        virtual void register_agent_for_user(const core::trust::Uid& uid, const std::shared_ptr<core::trust::Agent>& agent) = 0;
> +
> +        /** @brief Removes the agent for the given uid from the registry */
> +        virtual void unregister_agent_for_user(const core::trust::Uid& uid) = 0;
> +    };
> +
> +    /** @brief Summarizes all parameters for processing a trust request. */
> +    struct RequestParameters
> +    {
> +        /** @brief All application-specific parameters go here. */
> +        struct
> +        {
> +            /** @brief The user id under which the requesting application runs. */
> +            core::trust::Uid uid;
> +            /** @brief The process id of the requesting application. */
> +            core::trust::Pid pid;
> +            /** @brief The id of the requesting application. */
> +            std::string id;
> +        } application;
> +        /** @brief The service-specific feature identifier. */
> +        Feature feature;
> +        /** @brief An extended description that should be presented to the user on prompting. */
> +        std::string description;
> +    };
> +
>      /**
> -     * @brief Presents the given request to the user, returning the user-provided answer.
> -     * @param app_pid The process id of the requesting application.
> -     * @param app_id The application id of the requesting application.
> -     * @param description Extended description of the trust request.
> +     * @brief Authenticates the given request and returns the user's answer.
> +     * @param parameters [in] Describe the request.
>       */
> -    virtual Request::Answer prompt_user_for_request(pid_t app_pid, const std::string& app_id, const std::string& description) = 0;
> +    virtual Request::Answer authenticate_request_with_parameters(const RequestParameters& parameters) = 0;
>  };
> +
> +/** @brief Returns true iff lhs and rhs are equal. */
> +CORE_TRUST_DLL_PUBLIC bool operator==(const Agent::RequestParameters& lhs, const Agent::RequestParameters& rhs);
>  }
>  }
>  
> 
> === added file 'include/core/trust/cached_agent.h'
> --- include/core/trust/cached_agent.h	1970-01-01 00:00:00 +0000
> +++ include/core/trust/cached_agent.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,64 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_CACHED_AGENT_H_
> +#define CORE_TRUST_CACHED_AGENT_H_
> +
> +#include <core/trust/agent.h>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +/** @brief An agent implementation that uses a trust store instance to cache results. */
> +class CORE_TRUST_DLL_PUBLIC CachedAgent : public core::trust::Agent
> +{
> +public:
> +    /** @brief To safe some typing. */
> +    typedef std::shared_ptr<CachedAgent> Ptr;
> +
> +    /** @brief Creation time parameters. */
> +    struct Configuration
> +    {
> +        /** @brief The actual agent implementation for prompting the user. */
> +        std::shared_ptr<Agent> agent;
> +        /** @brief The store caching user answers to trust prompts. */
> +        std::shared_ptr<Store> store;
> +    };
> +
> +    /**
> +     * @brief CachedAgent creates a new agent instance.
> +     * @param configuration Specificies the actual agent and the store.
> +     * @throws std::logic_error if either the agent or the store are null.
> +     */
> +    CachedAgent(const Configuration& configuration);
> +    /** @cond */
> +    virtual ~CachedAgent() = default;
> +    /** @endcond */
> +
> +    /** @brief From core::trust::Agent. */
> +    Request::Answer authenticate_request_with_parameters(const core::trust::Agent::RequestParameters& parameters) override;
> +
> +private:
> +    /** @brief We just store a copy of the configuration parameters */
> +    Configuration configuration;
> +};
> +}
> +}
> +
> +#endif // CORE_TRUST_CACHED_AGENT_H_
> 
> === added file 'include/core/trust/dbus_agent.h'
> --- include/core/trust/dbus_agent.h	1970-01-01 00:00:00 +0000
> +++ include/core/trust/dbus_agent.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,62 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_DBUSAGENT_H_
> +#define CORE_TRUST_DBUSAGENT_H_
> +
> +#include <core/trust/visibility.h>
> +
> +namespace core
> +{
> +namespace dbus
> +{
> +class Bus;
> +}
> +namespace trust
> +{
> +// Forward declare the Agent interface.
> +class Agent;
> +
> +namespace dbus
> +{
> +/**
> + * @brief create_per_user_agent_for_bus_connection creates a trust::Agent implementation communicating with a remote agent
> + * implementation living in the same user session.
> + * @param connection An existing DBus connection.
> + * @param service The name of the service we are operating for.
> + * @throws std::runtime_error in case of issues.
> + */
> +CORE_TRUST_DLL_PUBLIC std::shared_ptr<core::trust::Agent> create_per_user_agent_for_bus_connection(
> +        const std::shared_ptr<core::dbus::Bus>& connection,
> +        const std::string& service_name);
> +
> +/**
> + * @brief create_agent_for_bus_connection creates a trust::Agent implementation communicating with user-specific

create_agent_for_bus_connection's docstring looks like it needs to be updated.

> + * remote agent implementations, living in user sessions.
> + * @param connection An existing DBus connection.
> + * @param service The name of the service we are operating for.
> + * @throws std::runtime_error in case of issues.
> + */
> +CORE_TRUST_DLL_PUBLIC std::shared_ptr<core::trust::Agent> create_multi_user_agent_for_bus_connection(
> +        const std::shared_ptr<core::dbus::Bus>& connection,
> +        const std::string& service_name);
> +}
> +}
> +}
> +
> +#endif // CORE_TRUST_DBUSAGENT_H_
> 
> === modified file 'include/core/trust/request.h'
> --- include/core/trust/request.h	2014-07-16 20:26:03 +0000
> +++ include/core/trust/request.h	2014-07-31 13:42:29 +0000
> @@ -19,6 +19,7 @@
>  #ifndef CORE_TRUST_REQUEST_H_
>  #define CORE_TRUST_REQUEST_H_
>  
> +#include <core/trust/tagged_integer.h>
>  #include <core/trust/visibility.h>
>  
>  #include <cstdint>
> @@ -61,7 +62,7 @@
>      static constexpr const unsigned int default_feature = 0;
>  
>      /** @brief Enumerates the possible answers given by a user. */
> -    enum class Answer
> +    enum class Answer : std::int32_t
>      {
>          denied, ///< Nope, I do not trust this application.
>          granted, ///< Yup, I do trust this application.
> @@ -70,7 +71,7 @@
>      /** The application id of the application that resulted in the request. */
>      std::string from;
>      /** An application-specific feature identifier. */
> -    std::uint64_t feature;
> +    Feature feature;
>      /** When the request happened in wallclock time. */
>      Timestamp when;
>      /** The user's answer. */
> @@ -107,12 +108,14 @@
>      std::shared_ptr<Agent> agent;
>      /** @brief The trust store to be used for caching purposes. */
>      std::shared_ptr<Store> store;
> +    /** @brief The user id under which the requesting application runs. */
> +    core::trust::Uid application_uid;
>      /** @brief The process id of the requesting application. */
> -    pid_t application_pid;
> +    core::trust::Pid application_pid;
>      /** @brief The id of the requesting application. */
>      std::string application_id;
>      /** @brief The service-specific feature identifier. */
> -    std::uint64_t feature;
> +    Feature feature;
>      /** @brief An extended description that should be presented to the user on prompting. */
>      std::string description;
>  };
> 
> === modified file 'include/core/trust/store.h'
> --- include/core/trust/store.h	2014-07-16 20:26:03 +0000
> +++ include/core/trust/store.h	2014-07-31 13:42:29 +0000
> @@ -20,6 +20,7 @@
>  #define CORE_TRUST_STORE_H_
>  
>  #include <core/trust/request.h>
> +#include <core/trust/tagged_integer.h>
>  #include <core/trust/visibility.h>
>  
>  #include <functional>
> @@ -131,7 +132,7 @@
>          virtual void for_application_id(const std::string& id) = 0;
>  
>          /** @brief Limit the query to a service-specific feature. */
> -        virtual void for_feature(std::uint64_t feature) = 0;
> +        virtual void for_feature(Feature feature) = 0;
>  
>          /** @brief Limit the query to the specified time interval. */
>          virtual void for_interval(const Request::Timestamp& begin, const Request::Timestamp& end) = 0;
> 
> === added file 'include/core/trust/tagged_integer.h'
> --- include/core/trust/tagged_integer.h	1970-01-01 00:00:00 +0000
> +++ include/core/trust/tagged_integer.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,110 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_TAGGED_INTEGER_H_
> +#define CORE_TRUST_TAGGED_INTEGER_H_
> +
> +#include <cstdint>
> +
> +#include <ostream>
> +#include <type_traits>
> +
> +#include <sys/types.h>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +/** @brief Helper structure for tagging integer types with certain semantics. */
> +template<typename Tag, typename Integer>
> +struct TaggedInteger
> +{
> +    /** @brief We bail out if the Integral type is not an integral one. */
> +    static_assert(std::is_integral<Integer>::value, "Integer has to be an integral type");
> +
> +    /** @brief Stores the Tag type. */
> +    typedef Tag TagType;
> +    /** @brief Stores the Integer type. */
> +    typedef Integer IntegerType;
> +
> +    /** @brief Construct an instance with a default value. */
> +    TaggedInteger() : value{}
> +    {
> +    }
> +
> +    /** @brief Construct an instance from an existing integer type. */
> +    explicit TaggedInteger(Integer value) : value{value}
> +    {
> +    }
> +
> +    /** @brief The contained integer value. */
> +    Integer value;
> +};
> +
> +/** @brief Returns true iff both tagged integer instances are equal. */
> +template<typename Tag, typename Integer>
> +inline bool operator==(const TaggedInteger<Tag, Integer>& lhs, const TaggedInteger<Tag, Integer>& rhs)
> +{
> +    return lhs.value == rhs.value;
> +}
> +
> +/** @brief Returns true iff both tagged integer instances are not equal. */
> +template<typename Tag, typename Integer>
> +inline bool operator!=(const TaggedInteger<Tag, Integer>& lhs, const TaggedInteger<Tag, Integer>& rhs)
> +{
> +    return lhs.value != rhs.value;
> +}
> +
> +/** @brief Returns true iff the left-hand-side integer instance is smaller than the right-hand-side. */
> +template<typename Tag, typename Integer>
> +inline bool operator<(const TaggedInteger<Tag, Integer>& lhs, const TaggedInteger<Tag, Integer>& rhs)
> +{
> +    return lhs.value < rhs.value;
> +}
> +
> +/** @brief Pretty prints a tagged integer. */
> +template<typename Tag, typename Integer>
> +inline std::ostream& operator<<(std::ostream& out, const TaggedInteger<Tag, Integer>& ti)
> +{
> +    return out << ti.value;
> +}
> +
> +namespace tag
> +{
> +// Tags a group id
> +struct Gid {};
> +// Tags a process id
> +struct Pid {};
> +// Tags a user id
> +struct Uid {};
> +// Tags a service-specific feature
> +struct Feature {};
> +}
> +
> +/** @brief Our internal group id type. */
> +typedef TaggedInteger<tag::Gid, gid_t> Gid;
> +/** @brief Our internal process id type. */
> +typedef TaggedInteger<tag::Pid, pid_t> Pid;
> +/** @brief Our internal user id type. */
> +typedef TaggedInteger<tag::Uid, uid_t> Uid;
> +/** @brief Our internal service-feature type. */
> +typedef TaggedInteger<tag::Feature, std::uint64_t> Feature;
> +}
> +}
> +
> +#endif // CORE_TRUST_TAGGED_INTEGER_H_
> 
> === modified file 'src/CMakeLists.txt'
> --- src/CMakeLists.txt	2014-07-16 10:42:32 +0000
> +++ src/CMakeLists.txt	2014-07-31 13:42:29 +0000
> @@ -20,11 +20,13 @@
>  
>  pkg_check_modules(DBUS_CPP dbus-cpp REQUIRED)
>  pkg_check_modules(DBUS dbus-1 REQUIRED)
> +pkg_check_modules(LIBAPPARMOR libapparmor REQUIRED)
>  pkg_check_modules(SQLITE3 sqlite3 REQUIRED)
>  
>  include_directories(
>    ${DBUS_CPP_INCLUDE_DIRS}
>    ${DBUS_INCLUDE_DIRS}
> +  ${LIBAPPARMOR_INCLUDE_DIRS}
>    ${SQLITE3_INCLUDE_DIRS}
>  )
>  
> @@ -37,18 +39,60 @@
>  add_library(
>    trust-store SHARED
>    
> +  core/trust/agent.cpp
>    core/trust/expose.cpp
>    core/trust/request.cpp
>    core/trust/resolve.cpp
>  
> +  # All dbus-specific headers go here
> +  core/trust/dbus/agent.h
> +  core/trust/dbus/agent_registry.h
> +  core/trust/dbus/codec.h
> +  core/trust/dbus/interface.h
> +
>    # The default implementation leverages SQLite3 to persist
>    # requests.
>    core/trust/impl/sqlite3/store.cpp
>  
> +  # An agent-implementation using a store instance to cache user replies.
> +  core/trust/cached_agent.cpp
> +
>    # An agent-implementation leveraging Mir's trusted prompting support
>    # to prompt the user for trusting an application to access a trusted
>    # system service.
>    core/trust/mir/agent.cpp
> +
> +  # Agent implementations for handling request out of process.
> +  core/trust/remote/agent.h
> +  core/trust/remote/agent.cpp
> +  core/trust/remote/helpers.h
> +  core/trust/remote/helpers.cpp
> +  # An implementation relying on dbus
> +  core/trust/remote/dbus.h
> +  core/trust/remote/dbus.cpp
> +  # An implementation relying on unix sockets, to be used for
> +  # inclusion with the android Camera Service.
> +  core/trust/remote/posix.h
> +  core/trust/remote/posix.cpp
> +)
> +
> +# Just a helper to avoid recompilation of the daemon
> +add_library(
> +  trust-stored
> +
> +  core/trust/daemon.cpp
> +)
> +
> +add_executable(
> +  trust-stored-skeleton
> +
> +  core/trust/daemon_skeleton_main.cpp
> +)
> +
> +add_executable(
> +  trust-stored-stub
> +
> +  core/trust/daemon_stub_main.cpp
>  )
>  
>  configure_file(
> @@ -81,6 +125,7 @@
>  
>    ${Boost_LIBRARIES}
>    ${DBUS_LIBRARIES}
> +  ${LIBAPPARMOR_LDFLAGS}
>    ${MIR_CLIENT_LDFLAGS}
>    ${MIR_COMMON_LDFLAGS}
>    ${PROCESS_CPP_LDFLAGS}
> @@ -88,6 +133,27 @@
>  )
>  
>  target_link_libraries(
> +  trust-stored
> +
> +  trust-store
> +
> +  ${MIR_CLIENT_LDFLAGS}
> +  ${MIR_COMMON_LDFLAGS}
> +)
> +
> +target_link_libraries(
> +  trust-stored-skeleton
> +
> +  trust-stored
> +)
> +
> +target_link_libraries(
> +  trust-stored-stub
> +
> +  trust-stored
> +)
> +
> +target_link_libraries(
>    trust-prompt
>  
>    ${Boost_LIBRARIES}
> @@ -116,6 +182,16 @@
>  )
>  
>  install(
> +  TARGETS trust-stored-skeleton
> +  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
> +)
> +
> +install(
> +  TARGETS trust-stored-stub
> +  RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}
> +)
> +
> +install(
>    TARGETS trust-prompt
>    RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}
>  )
> 
> === added file 'src/core/trust/agent.cpp'
> --- src/core/trust/agent.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/trust/agent.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,25 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/agent.h>
> +
> +bool core::trust::operator==(const core::trust::Agent::RequestParameters& lhs, const core::trust::Agent::RequestParameters& rhs)
> +{
> +    return std::tie(lhs.application.id, lhs.application.pid, lhs.application.uid, lhs.description, lhs.feature) ==
> +           std::tie(rhs.application.id, rhs.application.pid, rhs.application.uid, rhs.description, rhs.feature);
> +}
> 
> === added file 'src/core/trust/cached_agent.cpp'
> --- src/core/trust/cached_agent.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/trust/cached_agent.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,78 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/cached_agent.h>
> +
> +#include <core/trust/store.h>
> +
> +core::trust::CachedAgent::CachedAgent(const core::trust::CachedAgent::Configuration& configuration)
> +    : configuration(configuration)
> +{
> +    // We verify parameters first:
> +    if (not configuration.agent) throw std::logic_error
> +    {
> +        "Cannot operate without an agent implementation."
> +    };
> +
> +    if (not configuration.store) throw std::logic_error
> +    {
> +        "Cannot operate without a store implementation."
> +    };
> +}
> +
> +// From core::trust::Agent
> +core::trust::Request::Answer core::trust::CachedAgent::authenticate_request_with_parameters(
> +        const core::trust::Agent::RequestParameters& params)
> +{
> +    // Let's see if the store has an answer for app-id and feature.
> +    auto query = configuration.store->query();
> +
> +    // Narrow it down to the specific app and the specific feature
> +    query->for_application_id(params.application.id);
> +    query->for_feature(params.feature);
> +
> +    query->execute();
> +
> +    // We have got results and we take the most recent one as the most appropriate.
> +    if (query->status() == core::trust::Store::Query::Status::has_more_results)
> +    {
> +        // And we are returning early.
> +        return query->current().answer;
> +    }
> +
> +    // We do not have results available in the store, prompting the user
> +    auto answer = configuration.agent->authenticate_request_with_parameters(
> +                core::trust::Agent::RequestParameters
> +                {
> +                    params.application.uid,
> +                    params.application.pid,
> +                    params.application.id,
> +                    params.feature,
> +                    params.description
> +                });
> +
> +    configuration.store->add(core::trust::Request
> +    {
> +        params.application.id,
> +        params.feature,
> +        std::chrono::system_clock::now(),
> +        answer
> +    });
> +
> +    return answer;
> +}
> 
> === added file 'src/core/trust/daemon.cpp'
> --- src/core/trust/daemon.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/trust/daemon.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,587 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/daemon.h>
> +
> +#include <core/trust/cached_agent.h>
> +#include <core/trust/expose.h>
> +#include <core/trust/store.h>
> +
> +#include <core/trust/mir_agent.h>
> +#include <mirclient/mir_toolkit/mir_client_library.h>
> +
> +#include <core/trust/remote/agent.h>
> +#include <core/trust/remote/dbus.h>
> +#include <core/trust/remote/helpers.h>
> +#include <core/trust/remote/posix.h>
> +
> +#include <core/trust/terminal_agent.h>
> +
> +#include <core/dbus/asio/executor.h>
> +
> +#include <boost/asio.hpp>
> +#include <boost/exception/diagnostic_information.hpp>
> +#include <boost/program_options.hpp>
> +
> +#include <thread>
> +
> +namespace Options = boost::program_options;
> +
> +namespace
> +{
> +    struct Runtime
> +    {
> +        // Do not execute in parallel, serialize
> +        // accesses.
> +        static constexpr std::size_t concurrency_hint{2};
> +
> +        // Our evil singleton pattern. Not bad though, we control the
> +        // entire executable and rely on automatic cleanup of static
> +        // instances.
> +        static Runtime& instance()
> +        {
> +            static Runtime runtime;
> +            return runtime;
> +        }
> +
> +        ~Runtime()
> +        {
> +            io_service.stop();
> +
> +            if (worker1.joinable())
> +                worker1.join();
> +
> +            if (worker2.joinable())
> +                worker2.join();
> +        }
> +
> +        // We trap sig term to ensure a clean shutdown.
> +        std::shared_ptr<core::posix::SignalTrap> signal_trap
> +        {
> +            core::posix::trap_signals_for_all_subsequent_threads(
> +            {
> +                core::posix::Signal::sig_term,
> +                core::posix::Signal::sig_int
> +            })
> +        };
> +
> +        // Our io_service instance exposed to remote agents.
> +        boost::asio::io_service io_service
> +        {
> +            concurrency_hint
> +        };
> +
> +        // We keep the io_service alive and introduce some artifical
> +        // work.
> +        boost::asio::io_service::work keep_alive
> +        {
> +            io_service
> +        };
> +
> +        // We immediate execute the io_service instance
> +        std::thread worker1
> +        {
> +            std::thread{[this]() { io_service.run(); }}
> +        };
> +
> +        std::thread worker2
> +        {
> +            std::thread{[this]() { io_service.run(); }}
> +        };
> +    };
> +
> +    core::trust::Daemon::Dictionary fill_dictionary_from_unrecognized_options(const Options::parsed_options& parsed_options)
> +    {
> +        auto unrecognized = Options::collect_unrecognized(
> +                    parsed_options.options,
> +                    Options::exclude_positional);
> +
> +        core::trust::Daemon::Dictionary dict;
> +
> +        for (std::string& element : unrecognized)
> +        {
> +            if (element.find("--") != 0)
> +                continue;
> +
> +            auto idx = element.find("=");
> +
> +            if (idx == std::string::npos)
> +                continue;
> +
> +            auto key = element.substr(0, idx).substr(2, std::string::npos);
> +            auto value = element.substr(idx+1, std::string::npos);
> +
> +            dict[key] = value;
> +        }
> +
> +        return dict;
> +    }
> +
> +    struct DummyAgent : public core::trust::Agent
> +    {
> +        DummyAgent(core::trust::Request::Answer canned_answer)
> +            : canned_answer{canned_answer}
> +        {
> +        }
> +
> +        core::trust::Request::Answer authenticate_request_with_parameters(const RequestParameters&) override
> +        {
> +            return canned_answer;
> +        }
> +
> +        core::trust::Request::Answer canned_answer;
> +    };
> +
> +    core::dbus::Bus::Ptr bus_from_name(const std::string& bus_name)
> +    {
> +        core::dbus::Bus::Ptr bus;
> +
> +        if (bus_name == "system")
> +            bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::system);
> +        else if (bus_name == "session")
> +            bus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::session);
> +        else if (bus_name == "system_with_address_from_env")
> +            bus = std::make_shared<core::dbus::Bus>(core::posix::this_process::env::get_or_throw("DBUS_SYSTEM_BUS_ADDRESS"));
> +        else if (bus_name == "session_with_address_from_env")
> +            bus = std::make_shared<core::dbus::Bus>(core::posix::this_process::env::get_or_throw("DBUS_SESSION_BUS_ADDRESS"));
> +
> +        if (not bus) throw std::runtime_error
> +        {
> +            "Could not create bus for name: " + bus_name
> +        };
> +
> +        bus->install_executor(core::dbus::asio::make_executor(bus, Runtime::instance().io_service));
> +
> +        return bus;
> +    }
> +
> +    core::dbus::Bus::Ptr bus_from_dictionary(const core::trust::Daemon::Dictionary& dict)
> +    {
> +        return bus_from_name(dict.at("bus"));
> +    }
> +}
> +
> +const std::map<std::string, core::trust::Daemon::Skeleton::LocalAgentFactory>& core::trust::Daemon::Skeleton::known_local_agent_factories()
> +{
> +    static std::map<std::string, LocalAgentFactory> lut
> +    {
> +        {
> +            std::string{Daemon::LocalAgents::MirAgent::name},
> +            [](const std::string& service_name, const Dictionary& dict)
> +            {
> +                if (dict.count("trusted-mir-socket") == 0) throw std::runtime_error
> +                {
> +                    "Missing endpoint specification for accessing Mir's trusted socket."
> +                };
> +
> +                auto trusted_mir_socket = dict.at("trusted-mir-socket");
> +                auto mir_connection = mir_connect_sync(trusted_mir_socket.c_str(), service_name.c_str());
> +                auto agent = core::trust::mir::create_agent_for_mir_connection(mir_connection);
> +
> +                return agent;
> +            }
> +        },
> +        {
> +            std::string{Daemon::LocalAgents::TerminalAgent::name},
> +            [](const std::string& service_name, const Dictionary&)
> +            {
> +                return std::make_shared<core::trust::TerminalAgent>(service_name);
> +            }
> +        },
> +        {
> +            "TheAlwaysDenyingLocalAgent",
> +            [](const std::string&, const Dictionary&)
> +            {
> +                auto agent = std::shared_ptr<core::trust::Agent>
> +                {
> +                    new DummyAgent{core::trust::Request::Answer::denied}
> +                };
> +
> +                return agent;
> +            }
> +        }
> +    };
> +    return lut;
> +}
> +
> +const std::map<std::string, core::trust::Daemon::Skeleton::RemoteAgentFactory>& core::trust::Daemon::Skeleton::known_remote_agent_factories()
> +{
> +    static std::map<std::string, RemoteAgentFactory> lut
> +    {
> +        {
> +            std::string{Daemon::RemoteAgents::UnixDomainSocketRemoteAgent::name},
> +            [](const std::string& service_name, const std::shared_ptr<Agent>& agent, const Dictionary& dict)
> +            {
> +                if (dict.count("endpoint") == 0) throw std::runtime_error
> +                {
> +                    "Missing endpoint specification for UnixDomainSocketRemoteAgent."
> +                };
> +
> +                core::trust::remote::posix::Skeleton::Configuration config
> +                {
> +                    agent,
> +                    Runtime::instance().io_service,
> +                    boost::asio::local::stream_protocol::endpoint{dict.at("endpoint")},
> +                    core::trust::remote::helpers::proc_stat_start_time_resolver(),
> +                    core::trust::remote::helpers::aa_get_task_con_app_id_resolver(),
> +                    dict.count("description-pattern") > 0 ?
> +                            dict.at("description-pattern") :
> +                            "Application %1% is trying to access " + service_name + "."
> +                };
> +
> +                return core::trust::remote::posix::Skeleton::create_skeleton_for_configuration(config);
> +            }
> +        },
> +        {
> +            std::string{Daemon::RemoteAgents::DBusRemoteAgent::name},
> +            [](const std::string& service_name, const std::shared_ptr<Agent>& agent, const Dictionary& dict)
> +            {
> +                if (dict.count("bus") == 0) throw std::runtime_error
> +                {
> +                    "Missing bus specifier, please choose from {system, session}."
> +                };
> +
> +                auto bus = bus_from_dictionary(dict);
> +
> +                std::string dbus_service_name = core::trust::remote::dbus::default_service_name_prefix + std::string{"."} + service_name;
> +
> +                auto service = core::dbus::Service::use_service(bus, dbus_service_name);
> +                auto object = service->object_for_path(
> +                            core::dbus::types::ObjectPath
> +                            {
> +                                core::trust::remote::dbus::default_agent_registry_path
> +                            });
> +
> +                core::trust::remote::dbus::Agent::Skeleton::Configuration config
> +                {
> +                    agent,
> +                    object,
> +                    service,
> +                    bus,
> +                    core::trust::remote::helpers::aa_get_task_con_app_id_resolver()
> +                };
> +
> +                return std::make_shared<core::trust::remote::dbus::Agent::Skeleton>(config);
> +            }
> +        }
> +    };
> +    return lut;
> +}
> +
> +// Parses the configuration from the given command line.
> +core::trust::Daemon::Skeleton::Configuration core::trust::Daemon::Skeleton::Configuration::from_command_line(int argc, const char** argv)
> +{
> +    Options::variables_map vm;
> +    Dictionary dict;
> +
> +    Options::options_description options{"Known options"};
> +    options.add_options()
> +            (Parameters::ForService::name, Options::value<std::string>()->required(), Parameters::ForService::description)
> +            (Parameters::StoreBus::name, Options::value<std::string>()->default_value("session"), Parameters::StoreBus::description)
> +            (Parameters::LocalAgent::name, Options::value<std::string>()->required(), Parameters::LocalAgent::description)
> +            (Parameters::RemoteAgent::name, Options::value<std::string>()->required(), Parameters::RemoteAgent::description);
> +
> +    Options::command_line_parser parser
> +    {
> +        argc,
> +        argv
> +    };
> +
> +    try
> +    {
> +        auto parsed_options = parser.options(options).allow_unregistered().run();
> +        Options::store(parsed_options, vm);
> +        Options::notify(vm);
> +
> +        dict = fill_dictionary_from_unrecognized_options(parsed_options);
> +    } catch(const boost::exception& e)
> +    {
> +        throw std::runtime_error
> +        {
> +            "Error parsing command line: " + boost::diagnostic_information(e)
> +        };
> +    }
> +
> +    auto service_name = vm[Parameters::ForService::name].as<std::string>();
> +
> +    auto local_agent_factory = core::trust::Daemon::Skeleton::known_local_agent_factories()
> +            .at(vm[Parameters::LocalAgent::name].as<std::string>());
> +
> +    auto remote_agent_factory = core::trust::Daemon::Skeleton::known_remote_agent_factories()
> +            .at(vm[Parameters::RemoteAgent::name].as<std::string>());
> +
> +    auto local_store = core::trust::create_default_store(service_name);
> +    auto local_agent = local_agent_factory(service_name, dict);
> +
> +    auto cached_agent = std::make_shared<core::trust::CachedAgent>(
> +        core::trust::CachedAgent::Configuration
> +        {
> +            local_agent,
> +            local_store
> +        });
> +
> +    auto remote_agent = remote_agent_factory(service_name, cached_agent, dict);
> +
> +    return core::trust::Daemon::Skeleton::Configuration
> +    {
> +        service_name,
> +        bus_from_name(vm[Parameters::StoreBus::name].as<std::string>()),
> +        {local_store, local_agent},
> +        {remote_agent}
> +    };
> +}
> +
> +// Executes the daemon with the given configuration.
> +core::posix::exit::Status core::trust::Daemon::Skeleton::main(const core::trust::Daemon::Skeleton::Configuration& configuration)
> +{
> +    Runtime::instance().signal_trap->signal_raised().connect([](core::posix::Signal)
> +    {
> +        Runtime::instance().signal_trap->stop();
> +    });
> +
> +    std::thread worker
> +    {
> +        [configuration]() { configuration.bus->run(); }
> +    };
> +
> +    // Expose the local store to the bus, keeping it exposed for the
> +    // lifetime of the returned token.
> +    auto token = core::trust::expose_store_to_bus_with_name(
> +                configuration.local.store,
> +                configuration.bus,
> +                configuration.service_name);
> +
> +    Runtime::instance().signal_trap->run();
> +
> +    configuration.bus->stop();
> +
> +    if (worker.joinable())
> +        worker.join();
> +
> +    return core::posix::exit::Status::success;
> +}
> +
> +const std::map<std::string, core::trust::Daemon::Stub::RemoteAgentFactory>& core::trust::Daemon::Stub::known_remote_agent_factories()
> +{
> +    static std::map<std::string, RemoteAgentFactory> lut
> +    {
> +        {
> +            std::string{Daemon::RemoteAgents::UnixDomainSocketRemoteAgent::name},
> +            [](const std::string&, const Dictionary& dict)
> +            {
> +                if (dict.count("endpoint") == 0) throw std::runtime_error
> +                {
> +                    "Missing endpoint specification for UnixDomainSocketRemoteAgent."
> +                };
> +
> +                core::trust::remote::posix::Stub::Configuration config
> +                {
> +                    Runtime::instance().io_service,
> +                    boost::asio::local::stream_protocol::endpoint{dict.at("endpoint")},
> +                    core::trust::remote::helpers::proc_stat_start_time_resolver(),
> +                    core::trust::remote::posix::Stub::get_sock_opt_credentials_resolver(),
> +                    std::make_shared<core::trust::remote::posix::Stub::Session::Registry>()
> +                };
> +
> +                return core::trust::remote::posix::Stub::create_stub_for_configuration(config);
> +            }
> +        },
> +        {
> +            std::string{Daemon::RemoteAgents::DBusRemoteAgent::name},
> +            [](const std::string& service_name, const Dictionary& dict)
> +            {
> +                auto bus = bus_from_dictionary(dict);
> +
> +                std::string dbus_service_name = core::trust::remote::dbus::default_service_name_prefix + std::string{"."} + service_name;
> +
> +                auto service = core::dbus::Service::add_service(bus, dbus_service_name);
> +                auto object = service->add_object_for_path(
> +                            core::dbus::types::ObjectPath
> +                            {
> +                                core::trust::remote::dbus::default_agent_registry_path
> +                            });
> +
> +                core::trust::remote::dbus::Agent::Stub::Configuration config
> +                {
> +                    object,
> +                    bus
> +                };
> +
> +                return std::make_shared<core::trust::remote::dbus::Agent::Stub>(config);
> +            }
> +        }
> +    };
> +
> +    return lut;
> +}
> +
> +core::trust::Daemon::Stub::Configuration core::trust::Daemon::Stub::Configuration::from_command_line(int argc, const char** argv)
> +{
> +    Options::variables_map vm;
> +    Dictionary dict;
> +
> +    Options::options_description options{"Known options"};
> +    options.add_options()
> +            (Parameters::ForService::name, Options::value<std::string>()->required(), Parameters::ForService::description)
> +            (Parameters::RemoteAgent::name, Options::value<std::string>()->required(), Parameters::RemoteAgent::description);
> +
> +    Options::command_line_parser parser
> +    {
> +        argc,
> +        argv
> +    };
> +
> +    std::vector<std::string> unrecognized;
> +
> +    try
> +    {
> +        auto parsed_options = parser.options(options).allow_unregistered().run();
> +        Options::store(parsed_options, vm);
> +        Options::notify(vm);
> +
> +        dict = fill_dictionary_from_unrecognized_options(parsed_options);
> +    } catch(const boost::exception& e)
> +    {
> +        throw std::runtime_error
> +        {
> +            "Error parsing command line: " + boost::diagnostic_information(e)
> +        };
> +    }
> +
> +    auto service_name = vm[Parameters::ForService::name].as<std::string>();
> +
> +    auto remote_agent_factory = core::trust::Daemon::Stub::known_remote_agent_factories()
> +            .at(vm[Parameters::RemoteAgent::name].as<std::string>());
> +
> +    auto remote_agent = remote_agent_factory(service_name, dict);
> +
> +    return core::trust::Daemon::Stub::Configuration
> +    {
> +        service_name,
> +        remote_agent
> +    };
> +}
> +
> +namespace
> +{
> +// A very simple class to help with testing.
> +// A user can feed a request to the stub.
> +struct Shell : public std::enable_shared_from_this<Shell>
> +{
> +    Shell(const std::shared_ptr<core::trust::Agent>& agent)
> +        : agent{agent},
> +          stdin{Runtime::instance().io_service, STDIN_FILENO},
> +          app_id_resolver{core::trust::remote::helpers::aa_get_task_con_app_id_resolver()}
> +    {
> +    }
> +
> +    // Prints out the initial prompt and initiates a read operation on stdin.
> +    void start()
> +    {
> +        std::cout << "This is the super simple, interactive shell of the trust::store Damon" << std::endl;

Typo "Damon"

> +        std::cout << "The following commands are known:" << std::endl;
> +        std::cout << "  Enter a line like 'pid uid feature' to issue a query with the given parameters." << std::endl;
> +
> +        start_read();
> +    }
> +
> +    // Stops any outstanding read operation from stdin.
> +    void stop()
> +    {
> +        stdin.cancel();
> +    }
> +
> +    // Prints the shell prompt and starts an asynchronous read on stdin.
> +    void start_read()
> +    {
> +        // Our shell prompt.
> +        std::cout << "> " << std::flush;
> +
> +        // The async read operation
> +        boost::asio::async_read_until(
> +                    stdin,  // From stdin
> +                    buffer, // Into a buffer
> +                    '\n',   // Until we get a newline
> +                    boost::bind(
> +                        &Shell::read_finished,
> +                        shared_from_this(),
> +                        boost::asio::placeholders::error(),
> +                        boost::asio::placeholders::bytes_transferred()));
> +    }
> +
> +    // Invoked in case of errors or if one line has been read from stdin.
> +    void read_finished(const boost::system::error_code& ec, std::size_t)
> +    {
> +        if (ec == boost::asio::error::operation_aborted)
> +            return;
> +
> +        if (ec)
> +        {
> +            start_read();
> +            return;
> +        }
> +
> +        core::trust::Agent::RequestParameters params;
> +        std::istream ss{&buffer};
> +        ss >> params.application.pid.value >> params.application.uid.value >> params.feature.value;
> +
> +        // We fixup the parameters.
> +        params.application.id = app_id_resolver(params.application.pid);
> +
> +        std::cout << agent->authenticate_request_with_parameters(params) << std::endl;
> +
> +        buffer.consume(buffer.size());
> +
> +        start_read();
> +    }
> +
> +    // The agent we query for incoming requests.
> +    std::shared_ptr<core::trust::Agent> agent;
> +    // Async descriptor for reading from stdin.
> +    boost::asio::posix::stream_descriptor stdin;
> +    // The buffer we read in.
> +    boost::asio::streambuf buffer;
> +    // We use some helpers to fixup the quite limited requests we parse from the
> +    // command line
> +    core::trust::remote::helpers::AppIdResolver app_id_resolver;
> +};
> +}
> +
> +// Executes the daemon with the given configuration.
> +core::posix::exit::Status core::trust::Daemon::Stub::main(const core::trust::Daemon::Stub::Configuration& configuration)
> +{
> +    // We setup our minimal shell here.
> +    auto shell = std::make_shared<Shell>(configuration.remote.agent);
> +
> +    Runtime::instance().signal_trap->signal_raised().connect([](core::posix::Signal)
> +    {
> +        Runtime::instance().signal_trap->stop();
> +    });
> +
> +    // We start up our shell
> +    shell->start();
> +
> +    // Wait until signal arrives.
> +    Runtime::instance().signal_trap->run();
> +
> +    shell->stop();
> +
> +    return core::posix::exit::Status::success;
> +}
> +
> +
> 
> === added file 'src/core/trust/daemon.h'
> --- src/core/trust/daemon.h	1970-01-01 00:00:00 +0000
> +++ src/core/trust/daemon.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,248 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_DAEMON_H_
> +#define CORE_TRUST_DAEMON_H_
> +
> +#include <core/trust/agent.h>
> +#include <core/trust/store.h>
> +
> +#include <core/trust/remote/agent.h>
> +
> +#include <core/dbus/bus.h>
> +
> +#include <core/posix/exit.h>
> +
> +#include <functional>
> +#include <map>
> +#include <set>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +// Encapsulates an executable that allows services to run an out-of-process daemon for
> +// managing trust to applications. The reason here is simple: Patching each and every
> +// service in the system to use and link against the trust-store might not be feasible.
> +// In those cases, an out-of-process store would be started given the services name. The
> +// store exposes itself to the bus and subscribes to one or more request gates, processing
> +// the incoming requests by leveraging an agent implementation.
> +struct Daemon
> +{
> +    // Just to safe some typing.
> +    typedef std::map<std::string, std::string> Dictionary;
> +
> +    // Collects all known local agents
> +    struct LocalAgents
> +    {
> +        LocalAgents() = delete;
> +
> +        // Implements the trust::Agent interface and dispatches calls to a helper
> +        // prompt provider, tying it together with the requesting service and app
> +        // by leveraging Mir's trusted session/prompting support.
> +        struct MirAgent
> +        {
> +            static constexpr const char* name
> +            {
> +                "MirAgent"
> +            };
> +        };
> +
> +        // Implements the trust::Agent interface and dispatches calls to the user
> +        // leveraging whiptail to provide a dialog in a terminal.
> +        struct TerminalAgent
> +        {
> +            static constexpr const char* name
> +            {
> +                "TerminalAgent"
> +            };
> +        };
> +    };
> +
> +    // Collects all known remote agents
> +    struct RemoteAgents
> +    {
> +        RemoteAgents() = delete;
> +
> +        // A remote agent implementation that exposes a unix domain socket in the
> +        // filesystem. The stub implementation exposes the socket and handles incoming
> +        // connections from its skeletons. For incoming requests, the stub selects the
> +        // handling skeleton based on the user id associated with the request.
> +        struct UnixDomainSocketRemoteAgent
> +        {
> +            static constexpr const char* name
> +            {
> +                "UnixDomainSocketRemoteAgent"
> +            };
> +        };
> +
> +        // A remote agent implementation leveraging dbus.
> +        struct DBusRemoteAgent
> +        {
> +            static constexpr const char* name
> +            {
> +                "DBusRemoteAgent"
> +            };
> +        };
> +    };
> +
> +    struct Skeleton
> +    {
> +        // Functor for creating trust::Agent instances.
> +        typedef std::function<
> +            std::shared_ptr<core::trust::Agent>(
> +                const std::string&, // The name of the service.
> +                const Dictionary&)  // Dictionary containing Agent-specific configuration options.
> +        > LocalAgentFactory;
> +
> +        // Functor for creating trust::remote::Agent instances given a
> +        // set of parameters.
> +        typedef std::function<
> +            std::shared_ptr<core::trust::remote::Agent::Skeleton>(
> +                const std::string&,             // The name of the service.
> +                const std::shared_ptr<Agent>&,  // The local agent implementation.
> +                const Dictionary&               // Dictionary containing Agent-specific configuration options.
> +                )
> +        > RemoteAgentFactory;
> +
> +        // Returns a map for resolving names to local agent factories.
> +        static const std::map<std::string, LocalAgentFactory>& known_local_agent_factories();
> +
> +        // Returns a map for resolving names to remote agent factories.
> +        static const std::map<std::string, RemoteAgentFactory>&  known_remote_agent_factories();
> +
> +        // Command-line parameters, their name and their description
> +        struct Parameters
> +        {
> +            Parameters() = delete;
> +
> +            struct StoreBus
> +            {
> +                static constexpr const char* name{"store-bus"};
> +                static constexpr const char* description{"The bus that the remote store should be exposed on"};
> +            };
> +
> +            struct ForService
> +            {
> +                static constexpr const char* name{"for-service"};
> +                static constexpr const char* description{"The name of the service to handle trust for"};
> +            };
> +
> +            struct LocalAgent
> +            {
> +                static constexpr const char* name{"local-agent"};
> +                static constexpr const char* description{"The local agent implementation"};
> +            };
> +
> +            struct RemoteAgent
> +            {
> +                static constexpr const char* name{"remote-agent"};
> +                static constexpr const char* description{"The remote agent implementation"};
> +            };
> +        };
> +
> +        // Collects all parameters for executing the daemon
> +        struct Configuration
> +        {
> +            // Parses the configuration from the given command line.
> +            static Configuration from_command_line(int argc, const char** argv);
> +
> +            // The name of the service that the daemon should serve for.
> +            std::string service_name;
> +
> +            // The bus instance to expose the remote store instance on.
> +            core::dbus::Bus::Ptr bus;
> +
> +            // All local, i.e., actual implementations
> +            struct
> +            {
> +                // The store used for caching purposes.
> +                std::shared_ptr<Store> store;
> +                // The agent used for prompting the user.
> +                std::shared_ptr<Agent> agent;
> +            } local;
> +
> +            // All remote implementations for exposing the services
> +            // of this daemon instance.
> +            struct
> +            {
> +                std::shared_ptr<core::trust::remote::Agent::Skeleton> agent;
> +            } remote;
> +        };
> +
> +        // Executes the daemon with the given configuration.
> +        static core::posix::exit::Status main(const Configuration& configuration);
> +    };
> +
> +    struct Stub
> +    {
> +        // Functor for creating trust::Agent instances.
> +        typedef std::function<
> +            std::shared_ptr<core::trust::remote::Agent::Stub>(
> +                const std::string&, // The name of the service.
> +                const Dictionary&)  // Dictionary containing Agent-specific configuration options.
> +        > RemoteAgentFactory;
> +
> +        // Returns a map for resolving names to remote agent factories.
> +        static const std::map<std::string, RemoteAgentFactory>&  known_remote_agent_factories();
> +
> +        // Command-line parameters, their name and their description
> +        struct Parameters
> +        {
> +            Parameters() = delete;
> +
> +            struct ForService
> +            {
> +                static constexpr const char* name{"for-service"};
> +                static constexpr const char* description{"The name of the service to handle trust for"};
> +            };
> +
> +            struct RemoteAgent
> +            {
> +                static constexpr const char* name{"remote-agent"};
> +                static constexpr const char* description{"The remote agent implementation"};
> +            };
> +        };
> +
> +        // Collects all parameters for executing the daemon
> +        struct Configuration
> +        {
> +            // Parses the configuration from the given command line.
> +            static Configuration from_command_line(int argc, const char** argv);
> +
> +            // The name of the service that the daemon should serve for.
> +            std::string service_name;
> +
> +            // All remote agents.
> +            struct
> +            {
> +                // Trust requests are issued via this stub.
> +                std::shared_ptr<core::trust::remote::Agent::Stub> agent;
> +            } remote;
> +        };
> +
> +        // Executes the daemon with the given configuration.
> +        static core::posix::exit::Status main(const Configuration& configuration);
> +    };
> +};
> +}
> +}
> +
> +#endif // CORE_TRUST_DAEMON_H_
> +
> +
> 
> === added file 'src/core/trust/daemon_skeleton_main.cpp'
> --- src/core/trust/daemon_skeleton_main.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/trust/daemon_skeleton_main.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,37 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/daemon.h>
> +
> +#include <boost/exception/all.hpp>
> +
> +int main(int argc, const char** argv)
> +{
> +    core::trust::Daemon::Skeleton::Configuration configuration;
> +
> +    try
> +    {
> +        configuration = core::trust::Daemon::Skeleton::Configuration::from_command_line(argc, argv);
> +    } catch(const boost::exception& e)
> +    {
> +        std::cerr << "Error during initialization and startup: " << boost::diagnostic_information(e) << std::endl;
> +        return EXIT_FAILURE;
> +    }
> +
> +    return static_cast<int>(core::trust::Daemon::Skeleton::main(configuration));
> +}
> 
> === added file 'src/core/trust/daemon_stub_main.cpp'
> --- src/core/trust/daemon_stub_main.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/trust/daemon_stub_main.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,37 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/daemon.h>
> +
> +#include <boost/exception/all.hpp>
> +
> +int main(int argc, const char** argv)
> +{
> +    core::trust::Daemon::Stub::Configuration configuration;
> +
> +    try
> +    {
> +        configuration = core::trust::Daemon::Stub::Configuration::from_command_line(argc, argv);
> +    } catch(const boost::exception& e)
> +    {
> +        std::cerr << "Error during initialization and startup: " << boost::diagnostic_information(e) << std::endl;
> +        return EXIT_FAILURE;
> +    }
> +
> +    return static_cast<int>(core::trust::Daemon::Stub::main(configuration));
> +}
> 
> === added directory 'src/core/trust/dbus'
> === added file 'src/core/trust/dbus/agent.h'
> --- src/core/trust/dbus/agent.h	1970-01-01 00:00:00 +0000
> +++ src/core/trust/dbus/agent.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,175 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_DBUS_AGENT_H_
> +#define CORE_TRUST_DBUS_AGENT_H_
> +
> +#include <core/trust/agent.h>
> +#include <core/trust/dbus_agent.h>
> +
> +#include <core/trust/dbus/codec.h>
> +#include <core/trust/remote/helpers.h>
> +
> +#include <core/dbus/macros.h>
> +#include <core/dbus/object.h>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +namespace dbus
> +{
> +struct Agent
> +{
> +    static const std::string& name()
> +    {
> +        static const std::string s
> +        {
> +            "core.trust.dbus.Agent"
> +        };
> +        return s;
> +    }
> +
> +    struct Errors
> +    {
> +        struct CouldNotDetermineConclusiveAnswer : public std::runtime_error
> +        {
> +            static constexpr const char* name
> +            {
> +                "core.trust.dbus.Agent.Errors.CouldNotDetermineConclusiveAnswer"
> +            };
> +
> +            CouldNotDetermineConclusiveAnswer()
> +                : std::runtime_error("Could not determine conclusive answer to trust request.")
> +            {
> +            }
> +        };
> +    };
> +
> +    // Just for scoping purposes
> +    struct Methods
> +    {
> +        // And thus not constructible
> +        Methods() = delete;
> +
> +        DBUS_CPP_METHOD_WITH_TIMEOUT_DEF(AuthenticateRequestWithParameters, Agent, 10000)
> +    };
> +
> +    class Stub : public core::trust::Agent
> +    {
> +    public:
> +        Stub(const core::dbus::Object::Ptr& object)
> +            : object{object}
> +        {
> +        }
> +
> +        Request::Answer authenticate_request_with_parameters(const RequestParameters& parameters) override
> +        {
> +            auto result = object->transact_method
> +                    <
> +                        Methods::AuthenticateRequestWithParameters,
> +                        core::trust::Request::Answer
> +                    >(parameters);
> +
> +            if (result.is_error()) throw std::runtime_error
> +            {
> +                result.error().print()
> +            };
> +
> +            return result.value();
> +        }
> +
> +    private:
> +        core::dbus::Object::Ptr object;
> +    };
> +
> +    class Skeleton : public core::trust::Agent
> +    {
> +    public:
> +        // All creation time parameters go here.
> +        struct Configuration
> +        {
> +            // The object that should implement core.trust.Agent
> +            core::dbus::Object::Ptr object;
> +            // The underlying bus instance
> +            core::dbus::Bus::Ptr bus;
> +            // The agent implementation
> +            std::function<core::trust::Request::Answer(const core::trust::Agent::RequestParameters&)> agent;
> +        };
> +
> +        Skeleton(const Configuration& config)
> +            : configuration(config)
> +        {
> +            configuration.object->install_method_handler<Methods::AuthenticateRequestWithParameters>([this](const core::dbus::Message::Ptr& in)
> +            {
> +                core::trust::Agent::RequestParameters params; in->reader() >> params;
> +
> +                core::dbus::Message::Ptr reply;
> +                try
> +                {
> +                    reply = core::dbus::Message::make_method_return(in);
> +                    reply->writer() << authenticate_request_with_parameters(params);
> +                } catch(const std::exception& e)
> +                {
> +                    // TODO(tvoss): we should report the error locally here.
> +                    reply = core::dbus::Message::make_error(in, Errors::CouldNotDetermineConclusiveAnswer::name, "");
> +                } catch(...)
> +                {
> +                    reply = core::dbus::Message::make_error(in, Errors::CouldNotDetermineConclusiveAnswer::name, "");
> +                }
> +
> +                configuration.bus->send(reply);
> +            });
> +        }
> +
> +        Request::Answer authenticate_request_with_parameters(const RequestParameters& request) override
> +        {
> +            return configuration.agent(request);
> +        }
> +
> +    private:
> +        // We just store all creation time parameters.
> +        Configuration configuration;
> +    };
> +};
> +}
> +}
> +}
> +
> +/**
> + * @brief create_per_user_agent_for_bus_connection creates a trust::Agent implementation communicating with a remote agent
> + * implementation living in the same user session.
> + * @param connection An existing DBus connection.
> + * @param service The name of the service we are operating for.
> + * @throws std::runtime_error in case of issues.
> + */
> +std::shared_ptr<core::trust::Agent> core::trust::dbus::create_per_user_agent_for_bus_connection(
> +        const std::shared_ptr<core::dbus::Bus>& connection,
> +        const std::string& service_name)
> +{
> +    auto object =
> +            core::dbus::Service::use_service(connection,"core.trust.dbus.Agent." + service_name)
> +            ->object_for_path(core::dbus::types::ObjectPath{"/core/trust/dbus/Agent"});
> +
> +    return std::shared_ptr<core::trust::dbus::Agent::Stub>
> +    {
> +        new core::trust::dbus::Agent::Stub{object}
> +    };
> +}
> +
> +#endif // CORE_TRUST_DBUS_AGENT_H_
> 
> === added file 'src/core/trust/dbus/agent_registry.h'
> --- src/core/trust/dbus/agent_registry.h	1970-01-01 00:00:00 +0000
> +++ src/core/trust/dbus/agent_registry.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,332 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_DBUS_AGENT_REGISTRY_H_
> +#define CORE_TRUST_DBUS_AGENT_REGISTRY_H_
> +
> +#include <core/trust/dbus/agent.h>
> +#include <core/trust/dbus/codec.h>
> +
> +#include <core/dbus/macros.h>
> +#include <core/dbus/object.h>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +// A thread-safe AgentRegistry implementation.
> +class LockingAgentRegistry : public Agent::Registry
> +{
> +public:
> +    // From AgentRegistry
> +    void register_agent_for_user(const core::trust::Uid& uid, const std::shared_ptr<core::trust::Agent>& agent) override
> +    {
> +        std::lock_guard<std::mutex> lg(guard);
> +        registered_agents[uid] = agent;
> +    }
> +
> +    void unregister_agent_for_user(const core::trust::Uid& uid) override
> +    {
> +        std::lock_guard<std::mutex> lg(guard);
> +        registered_agents.erase(uid);
> +    }
> +
> +    // And some convenience
> +    // Returns true iff the registry knows about an agent for the given uid.
> +    bool has_agent_for_user(const core::trust::Uid& uid) const
> +    {
> +        std::lock_guard<std::mutex> lg(guard);
> +        return registered_agents.count(uid) > 0;
> +    }
> +
> +    // Returns the agent implementation for the given uid
> +    // or throws std::out_of_range if no agent is known for the uid.
> +    std::shared_ptr<Agent> agent_for_user(const core::trust::Uid& uid) const
> +    {
> +        std::lock_guard<std::mutex> lg(guard);
> +        return registered_agents.at(uid);
> +    }
> +
> +private:
> +    mutable std::mutex guard;
> +    std::map<core::trust::Uid, std::shared_ptr<core::trust::Agent>> registered_agents;
> +};
> +
> +namespace dbus
> +{
> +// Encapsulates stubs and skeletons for exposing/resolving an agent registry
> +// over the bus.
> +struct AgentRegistry
> +{
> +    // To safe some typing
> +    typedef std::shared_ptr<AgentRegistry> Ptr;
> +
> +    // The interface name
> +    static const std::string& name()
> +    {
> +        static const std::string s
> +        {
> +            "core.trust.dbus.AgentRegistry"
> +        };
> +        return s;
> +    }
> +
> +    // Encapsulates errors that can occur when
> +    // using the stub or the skeleton.
> +    struct Errors
> +    {
> +        // The remote implementation could not register the
> +        // agent for the given user.
> +        struct CouldNotRegisterAgentForUser
> +        {
> +            static constexpr const char* name
> +            {
> +                "core.trust.dbus.AgentRegistry.CouldNotRegisterAgentForUser"
> +            };
> +        };
> +    };
> +
> +    // Encapsulates all methods known to the remote end.
> +    struct Methods
> +    {
> +        // Not constructible.
> +        Methods() = delete;
> +
> +        // Maps to register_agent_for_user
> +        DBUS_CPP_METHOD_DEF(RegisterAgentForUser, AgentRegistry)
> +        // Maps to unregister_agent_for_user
> +        DBUS_CPP_METHOD_DEF(UnregisterAgentForUser, AgentRegistry)
> +    };
> +
> +    // A DBus stub implementation of core::trust::AgentRegistry.
> +    struct Stub : public core::trust::Agent::Registry
> +    {
> +        // Functor for generating unique object paths.
> +        typedef std::function<core::dbus::types::ObjectPath(const core::trust::Uid&)> ObjectPathGenerator;
> +
> +        static ObjectPathGenerator counting_object_path_generator()
> +        {
> +            return [](const core::trust::Uid&)
> +            {
> +                static int counter{0};
> +                return core::dbus::types::ObjectPath{"/core/trust/dbus/Agent/" + std::to_string(counter++)};
> +            };
> +        }
> +
> +        // All creation time parameters go here.
> +        struct Configuration
> +        {
> +            // The remote object implementing core.trust.dbus.AgentRegistry.
> +            core::dbus::Object::Ptr object;
> +            // The object generator instance.
> +            ObjectPathGenerator object_path_generator;
> +            // The local service instance to add objects to.
> +            core::dbus::Service::Ptr service;
> +            // The underlying bus instance.
> +            core::dbus::Bus::Ptr bus;
> +        };
> +
> +        // Initializes stub access to the Stub.
> +        Stub(const Configuration& configuration)
> +            : configuration(configuration)
> +        {
> +        }
> +
> +        // Calls into the remote implementation to register the given agent implementation.
> +        // Throws std::runtime_error and std::logic_error in case of issues.
> +        void register_agent_for_user(const core::trust::Uid& uid, const std::shared_ptr<core::trust::Agent>& impl) override
> +        {
> +            // We sample a path for the given uid
> +            auto path = configuration.object_path_generator(uid);
> +            // And construct the skeleton instance.
> +            auto skeleton = std::shared_ptr<core::trust::dbus::Agent::Skeleton>
> +            {
> +                new core::trust::dbus::Agent::Skeleton
> +                {
> +                    core::trust::dbus::Agent::Skeleton::Configuration
> +                    {
> +                        configuration.service->add_object_for_path(path),
> +                        configuration.bus,
> +                        std::bind(&core::trust::Agent::authenticate_request_with_parameters, impl, std::placeholders::_1)
> +                    }
> +                }
> +            };
> +
> +            // And announce the agent.
> +            auto result = configuration.object->transact_method<Methods::RegisterAgentForUser, void>(uid, path);
> +
> +            if (result.is_error()) throw std::runtime_error
> +            {
> +                result.error().print()
> +            };
> +
> +            // All good, finally updating our local registry.
> +            locking_agent_registry.register_agent_for_user(uid, skeleton);
> +        }
> +
> +        // Calls into the remote implementation to unregister any agent registered for the given uid.
> +        // Throws std::runtime_error and std::logic_error in case of issues.
> +        void unregister_agent_for_user(const core::trust::Uid& uid) override
> +        {
> +            auto result = configuration.object->transact_method<Methods::UnregisterAgentForUser, void>(uid);
> +
> +            // Updating our local registry even if the call to the remote end
> +            // might have failed.
> +            locking_agent_registry.unregister_agent_for_user(uid);
> +
> +            if (result.is_error()) throw std::runtime_error
> +            {
> +                result.error().print()
> +            };
> +        }
> +
> +        // We just store all creation-time arguments.
> +        Configuration configuration;
> +        // Our local registry of agents
> +        LockingAgentRegistry locking_agent_registry;
> +    };
> +
> +    // A DBus skeleton implementation of core::trust::AgentRegistry.
> +    struct Skeleton : public core::trust::Agent::Registry
> +    {
> +        // Creation time parameters go here.
> +        struct Configuration
> +        {
> +            // Object to install an implementation of
> +            // core.trust.dbus.AgentRegistry on.
> +            core::dbus::Object::Ptr object;
> +            // Bus-connection for sending out replies.
> +            core::dbus::Bus::Ptr bus;
> +            // The actual implementation.
> +            core::trust::Agent::Registry& impl;
> +        };
> +
> +        // Setups up handlers for:
> +        //   Methods::RegisterAgentForUser
> +        //   Methods::UnregisterAgentForUser
> +        Skeleton(const Configuration& config)
> +            : configuration(config)
> +        {
> +            configuration.object->install_method_handler<Methods::RegisterAgentForUser>([this](const core::dbus::Message::Ptr& in)
> +            {
> +                core::trust::Uid uid; core::dbus::types::ObjectPath path;
> +                in->reader() >> uid >> path;
> +
> +                // Assemble the stub Agent from the given parameters.
> +                auto service = core::dbus::Service::use_service(configuration.bus, in->sender());
> +                auto object = service->object_for_path(path);
> +
> +                std::shared_ptr<core::trust::dbus::Agent::Stub> agent
> +                {
> +                    new core::trust::dbus::Agent::Stub
> +                    {
> +                        object
> +                    }
> +                };
> +
> +                core::dbus::Message::Ptr reply;
> +
> +                // Forward the registration request to the actual implementation.
> +                try
> +                {
> +                    configuration.impl.register_agent_for_user(uid, agent);
> +                    reply = core::dbus::Message::make_method_return(in);
> +                } catch(const std::exception&)
> +                {
> +                    // TODO(tvoss): We should report the execption here.
> +                    // For now, we silently drop it and return an empty description
> +                    // to the sender to prevent from leaking any privacy/security-relevant
> +                    // data to the other side.
> +                    reply = core::dbus::Message::make_error(
> +                                in,
> +                                Errors::CouldNotRegisterAgentForUser::name,
> +                                "");
> +                } catch(...)
> +                {
> +                    reply = core::dbus::Message::make_error(
> +                                in,
> +                                Errors::CouldNotRegisterAgentForUser::name,
> +                                "");
> +                }
> +
> +                try
> +                {
> +                    configuration.bus->send(reply);
> +                } catch(const std::exception&)
> +                {
> +                    // TODO(tvoss): We should report the execption here.
> +                    // We immediately remove the agent for the given user id
> +                    // as the transaction to the other side did not complete.
> +                    unregister_agent_for_user(uid);
> +                } catch(...)
> +                {
> +                    // TODO(tvoss): We should report an issue here.
> +                    // We immediately remove the agent for the given user id
> +                    // as the transaction to the other side did not complete.
> +                    unregister_agent_for_user(uid);
> +                }
> +            });
> +
> +            configuration.object->install_method_handler<Methods::UnregisterAgentForUser>([this](const core::dbus::Message::Ptr& in)
> +            {
> +                core::trust::Uid uid; in->reader() >> uid;
> +
> +                try
> +                {
> +                    configuration.impl.unregister_agent_for_user(uid);
> +                } catch(const std::exception&)
> +                {
> +                    // Just dropping the exception here.
> +                    // We should definitely report any issues here, but
> +                    // stay silent for now.
> +                }
> +
> +                configuration.bus->send(core::dbus::Message::make_method_return(in));
> +            });
> +        }
> +
> +        // Uninstalls method handlers.
> +        ~Skeleton()
> +        {
> +            configuration.object->uninstall_method_handler<Methods::RegisterAgentForUser>();
> +            configuration.object->uninstall_method_handler<Methods::UnregisterAgentForUser>();
> +        }
> +
> +        // Registers an agent for the given uid,
> +        // calls out to the implementation given at construction time.
> +        void register_agent_for_user(const core::trust::Uid& uid, const std::shared_ptr<core::trust::Agent>& agent) override
> +        {
> +            configuration.impl.register_agent_for_user(uid, agent);
> +        }
> +
> +        // Removes the agent for the given uid from the registry,
> +        // calls out to the implementation given at construction time.
> +        void unregister_agent_for_user(const core::trust::Uid& uid) override
> +        {
> +            configuration.impl.unregister_agent_for_user(uid);
> +        }
> +
> +        // We just store all creation time parameters
> +        Configuration configuration;
> +    };
> +};
> +}
> +}
> +}
> +
> +#endif // CORE_TRUST_DBUS_AGENT_REGISTRY_H_
> 
> === renamed file 'src/core/trust/codec.h' => 'src/core/trust/dbus/codec.h'
> --- src/core/trust/codec.h	2014-05-06 11:32:13 +0000
> +++ src/core/trust/dbus/codec.h	2014-07-31 13:42:29 +0000
> @@ -19,6 +19,7 @@
>  #ifndef CORE_TRUST_CODEC_H_
>  #define CORE_TRUST_CODEC_H_
>  
> +#include <core/trust/agent.h>
>  #include <core/trust/request.h>
>  #include <core/trust/store.h>
>  
> @@ -31,7 +32,6 @@
>  {
>  namespace dbus
>  {
> -// Defines encoding and decoding of a trust request into a dbus message.
>  template<>
>  struct Codec<core::trust::Request::Answer>
>  {
> @@ -60,13 +60,27 @@
>      }
>  };
>  
> +template<typename Tag, typename Integer>
> +struct Codec<core::trust::TaggedInteger<Tag, Integer>>
> +{
> +    inline static void encode_argument(core::dbus::Message::Writer& writer, const core::trust::TaggedInteger<Tag, Integer>& arg)
> +    {
> +        writer.push_uint64(arg.value);
> +    }
> +
> +    inline static void decode_argument(core::dbus::Message::Reader& reader, core::trust::TaggedInteger<Tag, Integer>& arg)
> +    {
> +        arg.value = reader.pop_uint64();
> +    }
> +};
> +
>  template<>
>  struct Codec<core::trust::Request>
>  {
>      inline static void encode_argument(core::dbus::Message::Writer& writer, const core::trust::Request& arg)
>      {
>          writer.push_stringn(arg.from.c_str(), arg.from.size());
> -        writer.push_uint64(arg.feature);
> +        writer.push_uint64(arg.feature.value);
>          writer.push_uint64(std::chrono::duration_cast<core::trust::Request::Duration>(arg.when.time_since_epoch()).count());
>          Codec<core::trust::Request::Answer>::encode_argument(writer, arg.answer);
>      }
> @@ -74,11 +88,33 @@
>      inline static void decode_argument(core::dbus::Message::Reader& reader, core::trust::Request& arg)
>      {
>          arg.from = reader.pop_string();
> -        arg.feature = reader.pop_uint64();
> +        arg.feature.value = reader.pop_uint64();
>          arg.when = core::trust::Request::Timestamp{core::trust::Request::Duration{reader.pop_uint64()}};
>          Codec<core::trust::Request::Answer>::decode_argument(reader, arg.answer);
>      }
>  };
> +
> +template<>
> +struct Codec<core::trust::Agent::RequestParameters>
> +{
> +    inline static void encode_argument(core::dbus::Message::Writer& writer, const core::trust::Agent::RequestParameters& arg)
> +    {
> +        Codec<core::trust::Uid>::encode_argument(writer, arg.application.uid);
> +        Codec<core::trust::Pid>::encode_argument(writer, arg.application.pid);
> +        Codec<std::string>::encode_argument(writer, arg.application.id);
> +        Codec<core::trust::Feature>::encode_argument(writer, arg.feature);
> +        Codec<std::string>::encode_argument(writer, arg.description);
> +    }
> +
> +    inline static void decode_argument(core::dbus::Message::Reader& reader, core::trust::Agent::RequestParameters& arg)
> +    {
> +        Codec<core::trust::Uid>::decode_argument(reader, arg.application.uid);
> +        Codec<core::trust::Pid>::decode_argument(reader, arg.application.pid);
> +        Codec<std::string>::decode_argument(reader, arg.application.id);
> +        Codec<core::trust::Feature>::decode_argument(reader, arg.feature);
> +        Codec<std::string>::decode_argument(reader, arg.description);
> +    }
> +};
>  }
>  }
>  
> 
> === renamed file 'src/core/trust/dbus_interface.h' => 'src/core/trust/dbus/interface.h'
> === modified file 'src/core/trust/expose.cpp'
> --- src/core/trust/expose.cpp	2014-07-15 16:09:43 +0000
> +++ src/core/trust/expose.cpp	2014-07-31 13:42:29 +0000
> @@ -20,8 +20,8 @@
>  
>  #include <core/trust/store.h>
>  
> -#include "codec.h"
> -#include "dbus_interface.h"
> +#include "dbus/codec.h"
> +#include "dbus/interface.h"
>  
>  #include <core/dbus/asio/executor.h>
>  #include <core/dbus/codec.h>
> @@ -225,7 +225,7 @@
>              object->install_method_handler<core::trust::dbus::Store::Query::ForFeature>([this, query](const core::dbus::Message::Ptr& msg)
>              {
>                  std::uint64_t feature; msg->reader() >> feature;

I'm surprised the old uint64_t is used here rather than the core::trust::Feature type.

> -                query->for_feature(feature);
> +                query->for_feature(core::trust::Feature{feature});
>  
>                  auto reply = core::dbus::Message::make_method_return(msg);
>                  bus->send(reply);
> 
> === modified file 'src/core/trust/impl/sqlite3/store.cpp'
> --- src/core/trust/impl/sqlite3/store.cpp	2014-07-17 10:37:56 +0000
> +++ src/core/trust/impl/sqlite3/store.cpp	2014-07-31 13:42:29 +0000
> @@ -512,11 +512,11 @@
>              >(id);
>          }
>  
> -        void for_feature(std::uint64_t feature)
> +        void for_feature(core::trust::Feature feature)
>          {
>              d.select_statement.bind_int64<
>                  Statements::Select::Parameter::Feature::index
> -            >(feature);
> +            >(feature.value);
>          }
>  
>          void for_interval(const Request::Timestamp& begin, const Request::Timestamp& end)
> @@ -600,7 +600,10 @@
>                  trust::Request request
>                  {
>                      d.select_statement.column_text<Store::Table::Column::ApplicationId::index>(),
> -                    static_cast<unsigned int>(d.select_statement.column_int<Store::Table::Column::Feature::index>()),
> +                    trust::Feature
> +                    {
> +                        static_cast<trust::Feature::IntegerType>(d.select_statement.column_int<Store::Table::Column::Feature::index>())
> +                    },
>                      std::chrono::system_clock::time_point
>                      {
>                          std::chrono::system_clock::duration
> @@ -705,7 +708,7 @@
>  
>      insert_statement.reset();
>      insert_statement.bind_text<sqlite::Store::Table::Column::ApplicationId::index>(request.from);
> -    insert_statement.bind_int<sqlite::Store::Table::Column::Feature::index>(request.feature);
> +    insert_statement.bind_int<sqlite::Store::Table::Column::Feature::index>(request.feature.value);
>      insert_statement.bind_int64<sqlite::Store::Table::Column::Timestamp::index>(request.when.time_since_epoch().count());
>      insert_statement.bind_int<sqlite::Store::Table::Column::Answer::index>(static_cast<int>(request.answer));
>      insert_statement.step();
> 
> === modified file 'src/core/trust/mir/agent.cpp'
> --- src/core/trust/mir/agent.cpp	2014-07-16 20:26:03 +0000
> +++ src/core/trust/mir/agent.cpp	2014-07-31 13:42:29 +0000
> @@ -20,6 +20,10 @@
>  
>  #include "prompt_main.h"
>  
> +// For getuid
> +#include <unistd.h>
> +#include <sys/types.h>
> +
>  namespace mir = core::trust::mir;
>  
>  // Invoked whenever a request for creation of pre-authenticated fds succeeds.
> @@ -73,7 +77,7 @@
>  
>  mir::PromptSessionVirtualTable::Ptr mir::ConnectionVirtualTable::create_prompt_session_sync(
>          // The process id of the requesting app/service
> -        pid_t app_pid,
> +        core::trust::Pid app_pid,
>          // Callback handling prompt session state changes.
>          mir_prompt_session_state_change_callback cb,
>          // Callback context
> @@ -83,7 +87,7 @@
>      {
>          new PromptSessionVirtualTable
>          {
> -            mir_connection_create_prompt_session_sync(connection, app_pid, cb, context)
> +            mir_connection_create_prompt_session_sync(connection, app_pid.value, cb, context)
>          }
>      };
>  }
> @@ -181,8 +185,15 @@
>  }
>  
>  // From core::trust::Agent:
> -core::trust::Request::Answer mir::Agent::prompt_user_for_request(pid_t app_pid, const std::string& app_id, const std::string& description)
> +core::trust::Request::Answer mir::Agent::authenticate_request_with_parameters(const core::trust::Agent::RequestParameters& parameters)
>  {
> +    // We assume that the agent implementation runs under the same user id as
> +    // the requesting app to prevent from cross-user attacks.
> +    if (core::trust::Uid{::getuid()} != parameters.application.uid) throw std::logic_error
> +    {
> +        "mir::Agent::prompt_user_for_request: current user id does not match requesting app's user id"
> +    };
> +
>      // We initialize our callback context with an invalid child-process for setup
>      // purposes. Later on, once we have acquired a pre-authenticated fd for the
>      // prompt provider, we exec the actual provider in a child process and replace the
> @@ -201,7 +212,7 @@
>      {
>          // We setup the prompt session and wire up to our own internal callback helper.
>          connection_vtable->create_prompt_session_sync(
> -                    app_pid,
> +                    parameters.application.pid,
>                      Agent::on_trust_session_changed_state,
>                      &cb_context)
>      };
> @@ -213,8 +224,8 @@
>      mir::PromptProviderHelper::InvocationArguments args
>      {
>          fd,
> -        app_id,
> -        description
> +        parameters.application.id,
> +        parameters.description
>      };
>  
>      // Ask the helper to fire up the prompt provider.
> 
> === modified file 'src/core/trust/mir/agent.h'
> --- src/core/trust/mir/agent.h	2014-07-16 20:26:03 +0000
> +++ src/core/trust/mir/agent.h	2014-07-31 13:42:29 +0000
> @@ -88,7 +88,7 @@
>      // Creates a new trusted prompt session instance synchronously.
>      virtual PromptSessionVirtualTable::Ptr create_prompt_session_sync(
>              // The process id of the requesting app/service
> -            pid_t app_pid,
> +            Pid app_pid,
>              // Callback handling prompt session state changes.
>              mir_prompt_session_state_change_callback cb,
>              // Callback context
> @@ -178,7 +178,7 @@
>      // From core::trust::Agent:
>      // Throws a std::logic_error if anything unforeseen happens during execution, thus
>      // indicating that no conclusive answer could be obtained from the user.
> -    core::trust::Request::Answer prompt_user_for_request(pid_t app_pid, const std::string& app_id, const std::string& description) override;
> +    core::trust::Request::Answer authenticate_request_with_parameters(const RequestParameters& parameters) override;
>  
>      // The connection VTable used for creating trusted prompting sessions.
>      ConnectionVirtualTable::Ptr connection_vtable;
> 
> === modified file 'src/core/trust/mir/prompt_main.h'
> --- src/core/trust/mir/prompt_main.h	2014-07-17 15:37:13 +0000
> +++ src/core/trust/mir/prompt_main.h	2014-07-31 13:42:29 +0000
> @@ -27,6 +27,7 @@
>  {
>  namespace env
>  {
> +/** @brief Name of the environment variable to pass Mir's endpoint in. */
>  static constexpr const char* option_mir_socket
>  {
>      "MIR_SOCKET"
> 
> === added directory 'src/core/trust/remote'
> === added file 'src/core/trust/remote/agent.cpp'
> --- src/core/trust/remote/agent.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/trust/remote/agent.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include "core/trust/remote/agent.h"
> +
> +namespace trust = core::trust;
> +namespace remote = core::trust::remote;
> +
> +core::trust::Request::Answer remote::Agent::Stub::authenticate_request_with_parameters(const core::trust::Agent::RequestParameters& parameters)
> +{
> +    return send(parameters);
> +}
> +
> +remote::Agent::Skeleton::Skeleton(const std::shared_ptr<core::trust::Agent>& impl) : impl{impl}
> +{
> +}
> +
> +core::trust::Request::Answer remote::Agent::Skeleton::authenticate_request_with_parameters(const core::trust::Agent::RequestParameters& parameters)
> +{
> +    return impl->authenticate_request_with_parameters(parameters);
> +}
> 
> === added file 'src/core/trust/remote/agent.h'
> --- src/core/trust/remote/agent.h	1970-01-01 00:00:00 +0000
> +++ src/core/trust/remote/agent.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,74 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_REMOTE_AGENT_H_
> +#define CORE_TRUST_REMOTE_AGENT_H_
> +
> +#include <core/trust/agent.h>
> +#include <core/trust/request.h>
> +
> +#include <core/trust/visibility.h>
> +
> +#include <type_traits>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +namespace remote
> +{
> +// Abstracts listeners for incoming requests, possible implementations:
> +//   * Listening to a socket
> +//   * DBus
> +struct Agent
> +{
> +    // Models the sending end of a remote agent, meant to be used by trusted helpers.
> +    struct CORE_TRUST_DLL_PUBLIC Stub  : public core::trust::Agent
> +    {
> +        Stub() = default;
> +        virtual ~Stub() = default;
> +
> +        // From core::trust::Agent
> +        virtual Request::Answer authenticate_request_with_parameters(const RequestParameters& request);
> +
> +        // Sends out the request to the receiving end, either returning an answer
> +        // or throwing an exception if no conclusive answer could be obtained from
> +        // the user.
> +        virtual core::trust::Request::Answer send(const RequestParameters& parameters) = 0;
> +    };
> +
> +    // Models the receiving end of a remote agent, meant to be used by the trust store daemon.
> +    struct CORE_TRUST_DLL_PUBLIC  Skeleton : public core::trust::Agent
> +    {
> +        // Constructs a new Skeleton instance, installing impl for handling actual requests.
> +        Skeleton(const std::shared_ptr<Agent>& impl);
> +
> +        virtual ~Skeleton() = default;
> +
> +        // From core::trust::Agent, dispatches to the actual implementation.
> +        virtual core::trust::Request::Answer authenticate_request_with_parameters(const RequestParameters& parameters);
> +
> +        // The actual agent implementation that we are dispatching to.
> +        std::shared_ptr<core::trust::Agent> impl;
> +    };
> +};
> +}
> +}
> +}
> +
> +#endif // CORE_TRUST_REMOTE_AGENT_H_
> 
> === added file 'src/core/trust/remote/dbus.cpp'
> --- src/core/trust/remote/dbus.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/trust/remote/dbus.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,92 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/remote/dbus.h>
> +
> +#include <core/trust/dbus_agent.h>
> +
> +core::trust::remote::dbus::Agent::Stub::Stub(const core::trust::remote::dbus::Agent::Stub::Configuration& configuration)
> +    : agent_registry_skeleton
> +      {
> +          core::trust::dbus::AgentRegistry::Skeleton::Configuration
> +          {
> +              configuration.object,
> +              configuration.bus,
> +              agent_registry
> +          }
> +      }
> +{
> +}
> +
> +core::trust::Request::Answer core::trust::remote::dbus::Agent::Stub::send(const core::trust::Agent::RequestParameters& parameters)
> +{
> +    if (not agent_registry.has_agent_for_user(parameters.application.uid))
> +        return core::trust::Request::Answer::denied;
> +
> +    auto agent = agent_registry.agent_for_user(parameters.application.uid);
> +
> +    return agent->authenticate_request_with_parameters(parameters);
> +}
> +
> +core::trust::remote::dbus::Agent::Skeleton::Skeleton(const core::trust::remote::dbus::Agent::Skeleton::Configuration& configuration)
> +    : core::trust::remote::Agent::Skeleton{configuration.impl},
> +      agent_registry_stub
> +      {
> +          core::trust::dbus::AgentRegistry::Stub::Configuration
> +          {
> +              configuration.agent_registry_object,
> +              core::trust::dbus::AgentRegistry::Stub::counting_object_path_generator(),
> +              configuration.service,
> +              configuration.bus
> +          }
> +      }
> +{
> +    agent_registry_stub.register_agent_for_user(core::trust::Uid{::getuid()}, configuration.impl);
> +}
> +
> +core::trust::Request::Answer core::trust::remote::dbus::Agent::Skeleton::authenticate_request_with_parameters(const core::trust::Agent::RequestParameters& parameters)
> +{
> +    return remote::Agent::Skeleton::authenticate_request_with_parameters(parameters);
> +}
> +
> +std::shared_ptr<core::trust::Agent> core::trust::dbus::create_multi_user_agent_for_bus_connection(
> +        const std::shared_ptr<core::dbus::Bus>& connection,
> +        const std::string& service_name)
> +{
> +    auto dbus_service_name = core::trust::remote::dbus::default_service_name_prefix + std::string{"."} + service_name;
> +
> +    auto service = core::dbus::Service::add_service(connection, dbus_service_name);
> +    auto object = service->add_object_for_path(core::dbus::types::ObjectPath
> +    {
> +        core::trust::remote::dbus::default_agent_registry_path
> +    });
> +
> +    core::trust::remote::dbus::Agent::Stub::Configuration config
> +    {
> +        object,
> +        connection
> +    };
> +
> +    return std::shared_ptr<core::trust::remote::dbus::Agent::Stub>
> +    {
> +        new core::trust::remote::dbus::Agent::Stub
> +        {
> +            config
> +        }
> +    };
> +}
> 
> === added file 'src/core/trust/remote/dbus.h'
> --- src/core/trust/remote/dbus.h	1970-01-01 00:00:00 +0000
> +++ src/core/trust/remote/dbus.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,112 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_REMOTE_DBUS_AGENT_H_

This name doesn't match the filename.

> +#define CORE_TRUST_REMOTE_DBUS_AGENT_H_
> +
> +#include <core/trust/remote/agent.h>
> +#include <core/trust/remote/helpers.h>
> +
> +#include <core/trust/dbus/agent.h>
> +#include <core/trust/dbus/agent_registry.h>
> +
> +#include <unistd.h>
> +#include <sys/types.h>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +namespace remote
> +{
> +namespace dbus
> +{
> +constexpr const char* default_service_name_prefix
> +{
> +    "core.trust.dbus.Agent"
> +};
> +
> +constexpr const char* default_agent_registry_path
> +{
> +    "/core/trust/dbus/AgentRegistry"
> +};
> +
> +// Abstracts listeners for incoming requests, possible implementations:
> +//   * Listening to a socket
> +//   * DBus
> +struct CORE_TRUST_DLL_PUBLIC Agent
> +{
> +    // Models the sending end of a remote agent, meant to be used by trusted helpers.
> +    struct Stub : public core::trust::remote::Agent::Stub
> +    {
> +        // All creation time parameters go here.
> +        struct Configuration
> +        {
> +            // Object to install an implementation of
> +            // core.trust.dbus.AgentRegistry on.
> +            core::dbus::Object::Ptr object;
> +            // Bus-connection for sending out replies.
> +            core::dbus::Bus::Ptr bus;
> +        };
> +
> +        // Sets up the stub.
> +        Stub(const Configuration& configuration);
> +
> +        // Delivers the request described by the given parameters to the other side.
> +        core::trust::Request::Answer send(const RequestParameters& parameters) override;
> +
> +        // Our actual agent registry implementation.
> +        core::trust::LockingAgentRegistry agent_registry;
> +        // That we expose over the bus.
> +        core::trust::dbus::AgentRegistry::Skeleton agent_registry_skeleton;
> +    };
> +
> +    // Models the receiving end of a remote agent, meant to be used by the trust store daemon.
> +    struct Skeleton : public core::trust::remote::Agent::Skeleton
> +    {
> +        // All creation time parameters go here.
> +        struct Configuration
> +        {
> +            // The actual local agent implementation.
> +            std::shared_ptr<Agent> impl;
> +            // The remote object implementing core.trust.dbus.AgentRegistry.
> +            core::dbus::Object::Ptr agent_registry_object;
> +            // The sevice that objects implementing core.trust.dbus.Agent should be added to.
> +            core::dbus::Service::Ptr service;
> +            // The underlying bus instance.
> +            core::dbus::Bus::Ptr bus;
> +            // A helper for querying the application id for a given uid.
> +            helpers::AppIdResolver resolve_app_id;
> +        };
> +
> +        // Constructs a new Skeleton instance, installing impl for handling actual requests.
> +        Skeleton(const Configuration& configuration);
> +
> +        // From core::trust::Agent, dispatches to the actual implementation.
> +        core::trust::Request::Answer authenticate_request_with_parameters(const RequestParameters& parameters);
> +
> +        // Stub for accessing the remote agent registry.
> +        core::trust::dbus::AgentRegistry::Stub agent_registry_stub;
> +    };
> +};
> +}
> +}
> +}
> +}
> +
> +#endif // CORE_TRUST_REMOTE_DBUS_AGENT_H_
> 
> === added file 'src/core/trust/remote/helpers.cpp'
> --- src/core/trust/remote/helpers.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/trust/remote/helpers.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,79 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/remote/helpers.h>
> +
> +#include <core/posix/process.h>
> +#include <core/posix/linux/proc/process/stat.h>
> +
> +#include <sys/apparmor.h>
> +
> +namespace trust = core::trust;
> +namespace remote = core::trust::remote;
> +
> +// Queries the start time of a process by reading /proc/{PID}/stat.
> +remote::helpers::ProcessStartTimeResolver remote::helpers::proc_stat_start_time_resolver()
> +{
> +    return [](trust::Pid pid)
> +    {
> +        core::posix::Process process{pid.value};
> +        core::posix::linux::proc::process::Stat stat;
> +        process >> stat;
> +
> +        return stat.start_time;
> +    };
> +}
> +
> +remote::helpers::AppIdResolver remote::helpers::aa_get_task_con_app_id_resolver()
> +{
> +    return [](trust::Pid pid)
> +    {
> +        static const int app_armor_error{-1};
> +
> +        // We make sure to clean up the returned string.
> +        struct Scope
> +        {
> +            ~Scope()
> +            {
> +                if (con) ::free(con);
> +            }
> +
> +            char* con{nullptr};
> +            char* mode{nullptr};
> +        } scope;
> +
> +        // Reach out to apparmor
> +        auto rc = aa_gettaskcon(pid.value, &scope.con, &scope.mode);
> +
> +        // From man aa_gettaskcon:
> +        // On success size of data placed in the buffer is returned, this includes the mode if
> +        //present and any terminating characters. On error, -1 is returned, and errno(3) is
> +        //set appropriately.
> +        if (rc == app_armor_error) throw std::system_error
> +        {
> +            errno,
> +            std::system_category()
> +        };
> +
> +        // Safely construct the string
> +        return std::string
> +        {
> +            scope.con ? scope.con : ""
> +        };
> +    };
> +}
> 
> === added file 'src/core/trust/remote/helpers.h'
> --- src/core/trust/remote/helpers.h	1970-01-01 00:00:00 +0000
> +++ src/core/trust/remote/helpers.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,52 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_REMOTE_HELPERS_H_
> +#define CORE_TRUST_REMOTE_HELPERS_H_
> +
> +#include <core/trust/tagged_integer.h>
> +#include <core/trust/visibility.h>
> +
> +#include <functional>
> +#include <string>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +namespace remote
> +{
> +namespace helpers
> +{
> +// Functor abstracting pid -> process start time resolving
> +typedef std::function<std::int64_t(Pid)> ProcessStartTimeResolver;
> +
> +// Queries the start time of a process by reading /proc/{PID}/stat.
> +CORE_TRUST_DLL_PUBLIC ProcessStartTimeResolver proc_stat_start_time_resolver();
> +
> +// Functor abstracting pid -> app name resolving
> +typedef std::function<std::string(Pid)> AppIdResolver;
> +
> +// Queries the app armor confinement profile to resolve the application id.
> +CORE_TRUST_DLL_PUBLIC AppIdResolver aa_get_task_con_app_id_resolver();
> +}
> +}
> +}
> +}
> +
> +#endif // CORE_TRUST_REMOTE_HELPERS_H_
> 
> === added file 'src/core/trust/remote/posix.cpp'
> --- src/core/trust/remote/posix.cpp	1970-01-01 00:00:00 +0000
> +++ src/core/trust/remote/posix.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,333 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/remote/posix.h>
> +
> +#include <core/posix/process.h>
> +#include <core/posix/linux/proc/process/stat.h>
> +
> +#include <boost/asio.hpp>
> +#include <boost/bind.hpp>
> +#include <boost/exception/diagnostic_information.hpp>
> +#include <boost/format.hpp>
> +
> +#include <fstream>
> +#include <functional>
> +#include <mutex>
> +
> +namespace trust = core::trust;
> +namespace remote = core::trust::remote;
> +
> +remote::posix::Stub::PeerCredentialsResolver remote::posix::Stub::get_sock_opt_credentials_resolver()
> +{
> +    return [](int socket)
> +    {
> +        static const int get_sock_opt_error = -1;
> +
> +        ::ucred peer_credentials { 0, 0, 0 }; ::socklen_t len = sizeof(peer_credentials);
> +
> +        auto rc = ::getsockopt(socket, SOL_SOCKET, SO_PEERCRED, &peer_credentials, &len);
> +
> +        if (rc == get_sock_opt_error) throw std::system_error
> +        {
> +            errno, std::system_category()
> +        };
> +
> +        return std::make_tuple(
> +                    core::trust::Uid{peer_credentials.uid},
> +                    core::trust::Pid{peer_credentials.pid},
> +                    core::trust::Gid{peer_credentials.gid});
> +    };
> +}
> +
> +bool remote::posix::Stub::Session::Registry::has_session_for_uid(core::trust::Uid uid) const
> +{
> +    std::lock_guard<std::mutex> lg(guard);
> +    return sessions.count(uid) > 0;
> +}
> +
> +void remote::posix::Stub::Session::Registry::add_session_for_uid(core::trust::Uid uid, Session::Ptr session)
> +{
> +    if (not session) throw std::logic_error
> +    {
> +        "Cannot add null session to registry."
> +    };
> +
> +    std::lock_guard<std::mutex> lg(guard);
> +    sessions[uid] = session;
> +}
> +
> +void remote::posix::Stub::Session::Registry::remove_session_for_uid(core::trust::Uid uid)
> +{
> +    std::lock_guard<std::mutex> lg(guard);
> +    sessions.erase(uid);
> +}
> +
> +remote::posix::Stub::Session::Ptr remote::posix::Stub::Session::Registry::resolve_session_for_uid(core::trust::Uid uid)
> +{
> +    std::lock_guard<std::mutex> lg(guard);
> +    return sessions.at(uid);
> +}
> +
> +remote::posix::Stub::Session::Session(boost::asio::io_service& io_service)
> +    : socket{io_service}
> +{
> +}
> +
> +remote::posix::Stub::Ptr remote::posix::Stub::create_stub_for_configuration(const Configuration& config)
> +{
> +    remote::posix::Stub::Ptr stub
> +    {
> +        new remote::posix::Stub
> +        {
> +            config
> +        }
> +    };
> +
> +    stub->start_accept();
> +    return stub;
> +}
> +
> +// Creates a new instance with the given configuration.
> +// Throws in case of errors.
> +remote::posix::Stub::Stub(remote::posix::Stub::Configuration configuration)
> +    : io_service(configuration.io_service),
> +      end_point{configuration.endpoint},
> +      acceptor{io_service, end_point},
> +      start_time_resolver{configuration.start_time_resolver},
> +      peer_credentials_resolver{configuration.peer_credentials_resolver},
> +      session_registry{configuration.session_registry}
> +{
> +}
> +
> +void remote::posix::Stub::start_accept()
> +{
> +    remote::posix::Stub::Session::Ptr session
> +    {
> +        new remote::posix::Stub::Session{io_service}
> +    };
> +
> +    acceptor.async_accept(
> +                session->socket,
> +                boost::bind(&remote::posix::Stub::on_new_session,
> +                            shared_from_this(),
> +                            boost::asio::placeholders::error(),
> +                            session));
> +}
> +
> +remote::posix::Stub::~Stub()
> +{
> +    acceptor.cancel();
> +}
> +
> +core::trust::Request::Answer remote::posix::Stub::send(
> +        const core::trust::Agent::RequestParameters& parameters)
> +{
> +    // We consider the process start time to prevent from spoofing.
> +    auto start_time_before_query = start_time_resolver(parameters.application.pid);
> +
> +    // This call will throw if there is no session known for the uid.
> +    Session::Ptr session = session_registry->resolve_session_for_uid(parameters.application.uid);
> +
> +    remote::posix::Request request
> +    {
> +        parameters.application.uid,
> +        parameters.application.pid,
> +        parameters.feature,
> +        start_time_before_query
> +    };
> +
> +    boost::system::error_code ec;
> +
> +    boost::asio::write(
> +                session->socket,
> +                boost::asio::buffer(&request, sizeof(request)),
> +                ec);
> +
> +    if (ec)
> +        handle_error_from_socket_operation_for_uid(ec, parameters.application.uid);
> +
> +    core::trust::Request::Answer answer
> +    {
> +        core::trust::Request::Answer::denied
> +    };
> +
> +    boost::asio::read(
> +                session->socket,
> +                boost::asio::buffer(&answer, sizeof(answer)),
> +                ec);
> +
> +    if (ec)
> +        handle_error_from_socket_operation_for_uid(ec, parameters.application.uid);
> +
> +    // And finally, we check on the process start time.
> +    auto start_time_after_query = start_time_resolver(parameters.application.pid);
> +
> +    // We consider the process start time to prevent from spoofing. That is,
> +    // if the process start times differ here, we would authenticate a different process and
> +    // with that potentially a different app.
> +    if (start_time_before_query != start_time_after_query) throw std::runtime_error
> +    {
> +        "Detected a spoofing attempt, process start times before"
> +        "and after authentication do not match."
> +    };
> +
> +    // We will only ever return if we encountered no errors during communication
> +    // with the other side.
> +    return answer;
> +}
> +
> +// Called in case of an incoming connection.
> +void remote::posix::Stub::on_new_session(
> +        const boost::system::error_code& ec,
> +        const remote::posix::Stub::Session::Ptr& session)
> +{
> +    if (ec == boost::asio::error::operation_aborted)
> +        return;
> +
> +    if (ec) { start_accept(); return; }
> +
> +    auto pc = peer_credentials_resolver(session->socket.native_handle());
> +
> +    session_registry->add_session_for_uid(std::get<0>(pc), session);
> +
> +    start_accept();
> +}
> +
> +void remote::posix::Stub::handle_error_from_socket_operation_for_uid(const boost::system::error_code& ec, trust::Uid uid)
> +{
> +    switch (ec.value())
> +    {
> +    // We treat all the following errors as indications for the peer having
> +    // gone away. For that, we update the Session::Registry
> +    case boost::asio::error::basic_errors::access_denied:
> +    case boost::asio::error::basic_errors::broken_pipe:
> +    case boost::asio::error::basic_errors::connection_aborted:
> +    case boost::asio::error::basic_errors::connection_refused:
> +    case boost::asio::error::basic_errors::connection_reset:
> +        session_registry->remove_session_for_uid(uid);
> +        break;
> +    default:
> +        break;
> +    }
> +
> +    // By default, we just rethrow.
> +    throw std::system_error{ec.value(), std::system_category()};
> +}
> +
> +bool remote::posix::Stub::has_session_for_uid(trust::Uid uid) const
> +{
> +    return session_registry->has_session_for_uid(uid);
> +}
> +
> +remote::posix::Skeleton::Ptr remote::posix::Skeleton::create_skeleton_for_configuration(
> +    const remote::posix::Skeleton::Configuration& configuration)
> +{
> +    remote::posix::Skeleton::Ptr skeleton
> +    {
> +        new remote::posix::Skeleton
> +        {
> +            configuration
> +        }
> +    };
> +
> +    skeleton->start_read();
> +    return skeleton;
> +}
> +
> +remote::posix::Skeleton::Skeleton(const Configuration& configuration)
> +    : core::trust::remote::Agent::Skeleton{configuration.impl},
> +      start_time_resolver{configuration.start_time_resolver},
> +      app_id_resolver{configuration.app_id_resolver},
> +      description_pattern{configuration.description_format},
> +      endpoint{configuration.endpoint},
> +      socket{configuration.io_service}
> +{
> +    try
> +    {
> +        socket.connect(configuration.endpoint);
> +    } catch(const boost::exception& e)
> +    {
> +        throw std::runtime_error
> +        {
> +            "Could not connect to endpoint: " + endpoint.path()
> +        };
> +    }
> +}
> +
> +remote::posix::Skeleton::~Skeleton()
> +{
> +    socket.cancel();
> +}
> +
> +void remote::posix::Skeleton::start_read()
> +{
> +    Ptr sp{shared_from_this()};
> +
> +    boost::asio::async_read(
> +                socket,
> +                boost::asio::buffer(&request, sizeof(request)),
> +                [sp](const boost::system::error_code& ec, std::size_t size)
> +                {
> +                    sp->on_read_finished(ec, size);
> +                });
> +}
> +
> +void remote::posix::Skeleton::on_read_finished(const boost::system::error_code& ec, std::size_t size)
> +{
> +    if (ec == boost::asio::error::operation_aborted)
> +        return;
> +
> +    if (ec) { start_read(); return; }
> +
> +    if (size != sizeof(request))
> +        return;
> +
> +    auto answer = process_incoming_request(request);
> +
> +    // We send back our answer, we might throw here and just let the
> +    // exception propagate through the stack.
> +    boost::asio::write(socket, boost::asio::buffer(&answer, sizeof(answer)));
> +    // And restart reading.
> +    start_read();

Please confirm that this is a standard 'reactor pattern' type of reading arrangement and not an infinitely deep recursive loop that will eventually blow out its stack.

> +}
> +
> +core::trust::Request::Answer remote::posix::Skeleton::process_incoming_request(const core::trust::remote::posix::Request& request)
> +{
> +    // We first validate the process start time again.
> +    if (start_time_resolver(request.app_pid) != request.app_start_time) throw std::runtime_error
> +    {
> +        "Potential spoofing detected on incoming request."
> +    };
> +
> +    // Assemble the description.
> +    auto app_id = app_id_resolver(request.app_pid);
> +    auto description = (boost::format{description_pattern} % app_id).str();
> +
> +    // And reach out to the user.
> +    // TODO(tvoss): How to handle exceptions here?
> +
> +    return authenticate_request_with_parameters(core::trust::Agent::RequestParameters
> +    {
> +        request.app_uid,
> +        request.app_pid,
> +        app_id,
> +        request.feature,
> +        description
> +    });
> +}
> 
> === added file 'src/core/trust/remote/posix.h'
> --- src/core/trust/remote/posix.h	1970-01-01 00:00:00 +0000
> +++ src/core/trust/remote/posix.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,252 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_REMOTE_UNIX_DOMAIN_SOCKET_AGENT_H_

This name doesn't match the filename.

> +#define CORE_TRUST_REMOTE_UNIX_DOMAIN_SOCKET_AGENT_H_
> +
> +#include <core/trust/remote/agent.h>
> +#include <core/trust/remote/helpers.h>
> +
> +#include <core/posix/process.h>
> +#include <core/posix/linux/proc/process/stat.h>
> +
> +#include <sys/apparmor.h>
> +
> +#include <boost/asio.hpp>
> +#include <boost/bind.hpp>
> +#include <boost/format.hpp>
> +
> +#include <fstream>
> +#include <functional>
> +#include <mutex>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +namespace remote
> +{
> +// A remote agent implementation that exposes a unix domain socket in the
> +// filesystem. The stub implementation exposes the socket and handles incoming
> +// connections from its skeletons. For incoming requests, the stub selects the
> +// handling skeleton based on the user id associated with the request.
> +namespace posix
> +{
> +// Our distilled-down request that we share between stub and skeleton.
> +struct CORE_TRUST_DLL_PUBLIC Request
> +{
> +    // Id of the user that the requesting app is running under.
> +    Uid app_uid;
> +    // The process id of the requesting app.
> +    Pid app_pid;
> +    // The service-specific feature.
> +    Feature feature;
> +    // We want to prevent from spoofing and send over the process start time.
> +    // In seconds since the epoch.
> +    std::int64_t app_start_time;
> +};
> +
> +// Models the sending end of a remote agent, meant to be used by trusted helpers.
> +class CORE_TRUST_DLL_PUBLIC Stub
> +        : public core::trust::remote::Agent::Stub,
> +        public std::enable_shared_from_this<Stub>
> +{
> +public:
> +    // Just for convenience
> +    typedef std::shared_ptr<Stub> Ptr;
> +
> +    // Functor resolving a socket's peer's credentials
> +    typedef std::function<
> +        std::tuple<
> +            Uid,  // The user ID of the peer
> +            Pid,  // The process ID of the peer
> +            Gid   // The group ID of the peer
> +        >(int)      // Handle to the socket
> +    > PeerCredentialsResolver;
> +
> +    // Returns a peer credentials resolver that leverages getsockopt.
> +    static PeerCredentialsResolver get_sock_opt_credentials_resolver();
> +
> +    // We create a session per incoming connection.
> +    struct Session
> +    {
> +        // Just for convenience
> +        typedef std::shared_ptr<Session> Ptr;
> +
> +        // A fancy map implementing the monitor pattern.
> +        // All functions in this class are thread-safe, but _not_ reentrant.
> +        class Registry
> +        {
> +        public:
> +            typedef std::shared_ptr<Registry> Ptr;
> +
> +            Registry() = default;
> +            virtual ~Registry() = default;
> +
> +            // Returns true iff the registry instance contains a session for the given user id.
> +            virtual bool has_session_for_uid(Uid uid) const;
> +
> +            // Adds the given session for the given uid to the registry.
> +            virtual void add_session_for_uid(Uid uid, Session::Ptr session);
> +
> +            // Removes the session instance for the given user id.
> +            virtual void remove_session_for_uid(Uid uid);
> +
> +            // Queries the session for the given user id.
> +            // Throws std::out_of_range if no session is known for the user id.
> +            virtual Session::Ptr resolve_session_for_uid(Uid uid);
> +
> +        private:
> +            mutable std::mutex guard;
> +            std::map<core::trust::Uid, Session::Ptr> sessions;
> +        };
> +
> +        // Creates a new session.
> +        Session(boost::asio::io_service& io_service);
> +
> +        // The socket we are operating on.
> +        boost::asio::local::stream_protocol::socket socket;
> +        // We have to synchronize requests per session.
> +        std::mutex lock;
> +    };
> +
> +    // All creation time arguments go here.
> +    struct Configuration
> +    {
> +        // The runtime instance to associate to.
> +        boost::asio::io_service& io_service;
> +        // The endpoint in the filesystem.
> +        boost::asio::local::stream_protocol::endpoint endpoint;
> +        // Helper to map pid -> process start time
> +        helpers::ProcessStartTimeResolver start_time_resolver;
> +        // Helper to resolve peer credentials
> +        PeerCredentialsResolver peer_credentials_resolver;
> +        // A synchronized registry of all known sessions.
> +        Session::Registry::Ptr session_registry;
> +    };
> +
> +    // Creates a stub instance for the given configuration.
> +    // The returned instance is waiting for incoming connections.
> +    static Ptr create_stub_for_configuration(const Configuration& config);
> +
> +    // Frees all resources and cancels any outstanding async operation
> +    // on the endpoint.
> +    virtual ~Stub();
> +
> +    // From core::trust::remote::Agent::Stub.
> +    core::trust::Request::Answer send(const core::trust::Agent::RequestParameters& parameters) override;
> +
> +    // For testing purposes
> +    bool has_session_for_uid(Uid uid) const;
> +
> +private:
> +    // Creates a new instance with the given configuration.
> +    // Throws in case of errors.
> +    Stub(Configuration configuration);
> +
> +    // Starts listening on the endpoing configured at creation time
> +    // for incoming connections.
> +    void start_accept();
> +
> +    // Called in case of an incoming connection.
> +    void on_new_session(const boost::system::error_code& ec, const Session::Ptr& session);
> +
> +    // Interprets the given error code, carrying out maintenance on the Session::Registry
> +    // before rethrowing as a std::system_error.
> +    void handle_error_from_socket_operation_for_uid(const boost::system::error_code& ec, Uid uid);
> +
> +    // The io dispatcher that this instance is associated with.
> +    boost::asio::io_service& io_service;
> +    // The endpoint that this instance is bound to.
> +    boost::asio::local::stream_protocol::endpoint end_point;
> +    // The acceptor object for handling incoming connection requests.
> +    boost::asio::local::stream_protocol::acceptor acceptor;
> +
> +    // Helper to map pid -> process start time.
> +    helpers::ProcessStartTimeResolver start_time_resolver;
> +    // Helper for resolving a socket's peer's credentials.
> +    PeerCredentialsResolver peer_credentials_resolver;
> +    // A synchronized registry of all known sessions.
> +    Session::Registry::Ptr session_registry;
> +};
> +
> +// Models the receiving end of a remote agent, meant to be used by the trust store daemon.
> +class CORE_TRUST_DLL_PUBLIC Skeleton
> +        : public core::trust::remote::Agent::Skeleton,
> +        public std::enable_shared_from_this<Skeleton>
> +{
> +public:
> +    // Just for convenience
> +    typedef std::shared_ptr<Skeleton> Ptr;
> +
> +    // All creation-time arguments go here.
> +    struct Configuration
> +    {
> +        // The agent impl.
> +        std::shared_ptr<Agent> impl;
> +        // The runtime instance to associate to.
> +        boost::asio::io_service& io_service;
> +        // The endpoint in the filesystem.
> +        boost::asio::local::stream_protocol::endpoint endpoint;
> +        // Helper for resolving a pid to the process's start time.
> +        helpers::ProcessStartTimeResolver start_time_resolver;
> +        // Helper for resolving a pid to an application id.
> +        helpers::AppIdResolver app_id_resolver;
> +        // Pattern for assembling the prompt dialog's description given
> +        // an app id.
> +        std::string description_format;
> +    };
> +
> +    static Ptr create_skeleton_for_configuration(const Configuration& configuration);
> +
> +    virtual ~Skeleton();
> +
> +private:
> +    // Constructs a new Skeleton instance, installing impl for handling actual requests.
> +    Skeleton(const Configuration& configuration);
> +
> +    // Called to initiate an async read operation.
> +    void start_read();
> +
> +    // Called whenever a read operation from the socket finishes.
> +    void on_read_finished(const boost::system::error_code& ec, std::size_t size);
> +
> +    // Handles an incoming request, reaching out to the super class implementation
> +    // to obtain an answer to the trust request.
> +    core::trust::Request::Answer process_incoming_request(const posix::Request& request);
> +
> +    // Request object that we read into.
> +    posix::Request request;
> +    // Helper for resolving pid -> start time.
> +    helpers::ProcessStartTimeResolver start_time_resolver;
> +    // Helper for resolving pid -> application id.
> +    helpers::AppIdResolver app_id_resolver;
> +    // Pattern for assembling the prompt dialog's description given
> +    // an app id.
> +    std::string description_pattern;
> +    // The endpoint in the filesystem that we are connected with.
> +    boost::asio::local::stream_protocol::endpoint endpoint;
> +    // The actual socket for communication with the service.
> +    boost::asio::local::stream_protocol::socket socket;
> +};
> +}
> +}
> +}
> +}
> +
> +#endif // CORE_TRUST_REMOTE_AGENT_H_
> 
> === modified file 'src/core/trust/request.cpp'
> --- src/core/trust/request.cpp	2014-07-14 16:05:37 +0000
> +++ src/core/trust/request.cpp	2014-07-31 13:42:29 +0000
> @@ -51,10 +51,14 @@
>      }
>  
>      // We do not have results available in the store, prompting the user
> -    auto answer = params.agent->prompt_user_for_request(
> -                params.application_pid,
> -                params.application_id,
> -                params.description);
> +    auto answer = params.agent->authenticate_request_with_parameters(core::trust::Agent::RequestParameters
> +    {
> +        params.application_uid,
> +        params.application_pid,
> +        params.application_id,
> +        params.feature,
> +        params.description
> +    });
>  
>      params.store->add(core::trust::Request
>      {
> 
> === modified file 'src/core/trust/resolve.cpp'
> --- src/core/trust/resolve.cpp	2014-07-25 08:23:56 +0000
> +++ src/core/trust/resolve.cpp	2014-07-31 13:42:29 +0000
> @@ -21,8 +21,8 @@
>  #include <core/trust/request.h>
>  #include <core/trust/store.h>
>  
> -#include "codec.h"
> -#include "dbus_interface.h"
> +#include "dbus/codec.h"
> +#include "dbus/interface.h"
>  
>  #include <core/dbus/asio/executor.h>
>  
> @@ -142,7 +142,7 @@
>                  throw std::runtime_error(result.error().print());
>          }
>  
> -        void for_feature(std::uint64_t feature)
> +        void for_feature(core::trust::Feature feature)
>          {
>              auto result = object->invoke_method_synchronously<core::trust::dbus::Store::Query::ForFeature, void>(feature);
>  
> 
> === added file 'src/core/trust/terminal_agent.h'
> --- src/core/trust/terminal_agent.h	1970-01-01 00:00:00 +0000
> +++ src/core/trust/terminal_agent.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,101 @@
> +/*
> + * Copyright © 2014 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef CORE_TRUST_TERMINAL_AGENT_H_
> +#define CORE_TRUST_TERMINAL_AGENT_H_
> +
> +#include <core/trust/agent.h>
> +
> +#include <core/posix/exec.h>
> +
> +#include <iterator>
> +
> +namespace core
> +{
> +namespace trust
> +{
> +// Agent implementation leveraging whiptail to display
> +// a dialog box in the terminal.
> +struct TerminalAgent : public core::trust::Agent
> +{
> +    // Default width of the dialog box
> +    static constexpr int default_width{70};
> +    // Default height of the dialog box
> +    static constexpr int default_height{10};
> +
> +    // Path to the whiptail executable.
> +    static constexpr const char* whiptail
> +    {
> +        "/bin/whiptail"
> +    };
> +
> +    // Constructs a new instance for the given service name.
> +    TerminalAgent(const std::string& service_name) : service_name{service_name}
> +    {
> +    }
> +
> +    // From core::trust::Agent.
> +    Request::Answer authenticate_request_with_parameters(const RequestParameters& parameters) override
> +    {
> +        std::map<std::string, std::string> env;
> +
> +        core::posix::this_process::env::for_each([&env](const std::string& k, const std::string& v)
> +        {
> +            env.insert(std::make_pair(k, v));
> +        });
> +
> +        std::vector<std::string> args
> +        {
> +            "--title", "\"Please audit access to: " + service_name + " by " + parameters.application.id + "\"",
> +            "--yes-button", "Grant",
> +            "--no-button", "Deny",
> +            "--yesno",
> +            "\"" + parameters.description + "\"", std::to_string(default_height), std::to_string(default_width)
> +        };
> +
> +        std::copy(args.begin(), args.end(), std::ostream_iterator<std::string>(std::cout, " "));
> +
> +        auto whiptail_process = core::posix::exec(
> +                    whiptail,
> +                    args,
> +                    env,
> +                    core::posix::StandardStream::empty);
> +
> +        auto result = whiptail_process.wait_for(core::posix::wait::Flags::untraced);
> +
> +        // We now analyze the result of the process execution.
> +        if (core::posix::wait::Result::Status::exited != result.status) throw std::logic_error
> +        {
> +            "Unable to determine a conclusive answer from the user"
> +        };
> +
> +        // If the child process did not exit cleanly, we deny access to the resource.
> +        if (core::posix::exit::Status::failure == result.detail.if_exited.status)
> +            return core::trust::Request::Answer::denied;
> +
> +        return core::trust::Request::Answer::granted;
> +    }
> +
> +    // The name of the service we are acting for.
> +    std::string service_name;
> +};
> +}
> +}
> +
> +#endif // CORE_TRUST_TERMINAL_AGENT_H_
> +
> 
> === modified file 'tests/CMakeLists.txt'
> --- tests/CMakeLists.txt	2014-07-18 09:24:07 +0000
> +++ tests/CMakeLists.txt	2014-07-31 13:42:29 +0000
> @@ -43,6 +43,26 @@
>    mir_agent_test.cpp
>  )
>  
> +add_executable(
> +  remote_agent_test
> +  remote_agent_test.cpp
> +)
> +
> +add_executable(
> +  cached_agent_test
> +  cached_agent_test.cpp
> +)
> +
> +add_executable(
> +  daemon_test
> +  daemon_test.cpp
> +)
> +
> +add_executable(
> +  dbus_test
> +  dbus_test.cpp
> +)
> +
>  target_link_libraries(
>    trust_store_test
>  
> @@ -93,12 +113,70 @@
>    ${PROCESS_CPP_LIBRARIES}
>  )
>  
> +target_link_libraries(
> +  remote_agent_test
> +
> +  trust-store
> +
> +  gmock
> +
> +  gtest
> +  gtest_main
> +
> +  ${PROCESS_CPP_LIBRARIES}
> +)
> +
> +target_link_libraries(
> +  cached_agent_test
> +
> +  trust-store
> +
> +  gmock
> +
> +  gtest
> +  gtest_main
> +
> +  ${PROCESS_CPP_LIBRARIES}
> +)
> +
> +target_link_libraries(
> +  daemon_test
> +
> +  trust-store
> +  trust-stored
> +
> +  gmock
> +
> +  gtest
> +  gtest_main
> +
> +  ${PROCESS_CPP_LIBRARIES}
> +)
> +
> +target_link_libraries(
> +  dbus_test
> +
> +  trust-store
> +
> +  gmock
> +
> +  gtest
> +  gtest_main
> +
> +  ${PROCESS_CPP_LIBRARIES}
> +)
> +
>  add_test(trust_store_test ${CMAKE_CURRENT_BINARY_DIR}/trust_store_test)
>  add_test(remote_trust_store_test ${CMAKE_CURRENT_BINARY_DIR}/remote_trust_store_test)
>  add_test(request_processor_test ${CMAKE_CURRENT_BINARY_DIR}/request_processor_test)
>  add_test(mir_agent_test ${CMAKE_CURRENT_BINARY_DIR}/mir_agent_test --gtest_filter=*-*requires_mir)
> +add_test(remote_agent_test ${CMAKE_CURRENT_BINARY_DIR}/remote_agent_test)
> +add_test(cached_agent_test ${CMAKE_CURRENT_BINARY_DIR}/cached_agent_test)
> +# TODO(tvoss) Re-enable daemon tests once CI issues are resolved.
> +# add_test(daemon_test ${CMAKE_CURRENT_BINARY_DIR}/daemon_test)
> +add_test(dbus_test ${CMAKE_CURRENT_BINARY_DIR}/dbus_test)
>  
>  install(
> -  TARGETS trust_store_test remote_trust_store_test request_processor_test mir_agent_test
> +  TARGETS trust_store_test remote_trust_store_test request_processor_test mir_agent_test remote_agent_test cached_agent_test daemon_test dbus_test
>    RUNTIME DESTINATION ${CMAKE_INSTALL_LIBDIR}/trust-store-tests
>  )
> 
> === added file 'tests/cached_agent_test.cpp'
> --- tests/cached_agent_test.cpp	1970-01-01 00:00:00 +0000
> +++ tests/cached_agent_test.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,250 @@
> +/*
> + * Copyright © 2013 Canonical Ltd.

2013 copyright date; several other files are the same.

> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/cached_agent.h>
> +
> +#include "mock_agent.h"
> +#include "mock_store.h"
> +
> +namespace
> +{
> +core::trust::Pid the_default_pid_for_testing()
> +{
> +    return core::trust::Pid{42};
> +}
> +
> +core::trust::Uid the_default_uid_for_testing()
> +{
> +    return core::trust::Uid{42};
> +}
> +
> +core::trust::Feature the_default_feature_for_testing()
> +{
> +    return core::trust::Feature{0};
> +}
> +
> +std::shared_ptr<core::trust::Agent> a_null_agent()
> +{
> +    return std::shared_ptr<core::trust::Agent>{};
> +}
> +
> +std::shared_ptr<testing::NiceMock<MockAgent>> a_mocked_agent()
> +{
> +    return std::make_shared<testing::NiceMock<MockAgent>>();
> +}
> +
> +std::shared_ptr<core::trust::Store> a_null_store()
> +{
> +    return std::shared_ptr<core::trust::Store>{};
> +}
> +
> +std::shared_ptr<testing::NiceMock<MockStore>> a_mocked_store()
> +{
> +    return std::make_shared<testing::NiceMock<MockStore>>();
> +}
> +
> +std::shared_ptr<testing::NiceMock<MockStore::MockQuery>> a_mocked_query()
> +{
> +    return std::make_shared<testing::NiceMock<MockStore::MockQuery>>();
> +}
> +
> +core::trust::Agent::RequestParameters default_request_parameters_for_testing()
> +{
> +    return core::trust::Agent::RequestParameters
> +    {
> +        the_default_uid_for_testing(),
> +        the_default_pid_for_testing(),
> +        "this.is.just.for.testing.purposes",
> +        the_default_feature_for_testing(),
> +        "Someone wants to access all your credentials and steal your identity."
> +    };
> +}
> +
> +core::trust::Request::Answer throw_a_dice()
> +{
> +    // We seed the rng with the current time to ensure randomness across test runs.
> +    static std::default_random_engine generator
> +    {
> +        static_cast<long unsigned int>(std::chrono::system_clock::now().time_since_epoch().count())
> +    };
> +    // Our dice :)
> +    static std::uniform_int_distribution<int> distribution
> +    {
> +        1,
> +        6
> +    };
> +
> +    return distribution(generator) <= 3 ?
> +                core::trust::Request::Answer::denied :
> +                core::trust::Request::Answer::granted;
> +}
> +
> +}
> +
> +TEST(CachedAgent, ctor_throws_for_missing_agent_implementation)
> +{
> +    core::trust::CachedAgent::Configuration configuration
> +    {
> +        a_null_agent(),
> +        a_mocked_store()
> +    };
> +
> +    EXPECT_THROW(core::trust::CachedAgent agent{configuration},
> +                 std::logic_error);
> +}
> +
> +TEST(RequestProcessing, ctor_throws_for_missing_store_implementation)
> +{
> +    core::trust::CachedAgent::Configuration configuration
> +    {
> +        a_mocked_agent(),
> +        a_null_store()
> +    };
> +
> +    EXPECT_THROW(core::trust::CachedAgent agent{configuration},
> +                 std::logic_error);
> +}
> +
> +TEST(CachedAgent, queries_store_for_cached_results_and_returns_cached_value)
> +{
> +    using namespace ::testing;
> +
> +    auto answer = throw_a_dice();
> +
> +    auto params = default_request_parameters_for_testing();
> +
> +    core::trust::Request request
> +    {
> +        params.application.id,
> +        params.feature,
> +        std::chrono::system_clock::now(),
> +        answer
> +    };
> +
> +    auto mocked_agent = a_mocked_agent();
> +    auto mocked_query = a_mocked_query();
> +    auto mocked_store = a_mocked_store();
> +
> +    ON_CALL(*mocked_query, status())
> +            .WillByDefault(
> +                Return(
> +                    core::trust::Store::Query::Status::has_more_results));
> +
> +    ON_CALL(*mocked_query, current())
> +            .WillByDefault(
> +                Return(
> +                    request));
> +
> +    ON_CALL(*mocked_store, query())
> +            .WillByDefault(
> +                Return(
> +                    mocked_query));
> +
> +    EXPECT_CALL(*mocked_store, query()).Times(1);
> +    // We expect the processor to limit the query to the respective application id
> +    // and to the respective feature.
> +    EXPECT_CALL(*mocked_query, for_application_id(params.application.id)).Times(1);
> +    EXPECT_CALL(*mocked_query, for_feature(params.feature)).Times(1);
> +    // The setup ensures that a previously stored answer is available in the store.
> +    // For that, the agent should not be queried.
> +    EXPECT_CALL(*mocked_agent, authenticate_request_with_parameters(_)).Times(0);
> +
> +    core::trust::CachedAgent::Configuration configuration
> +    {
> +        mocked_agent,
> +        mocked_store
> +    };
> +
> +    core::trust::CachedAgent agent
> +    {
> +        configuration
> +    };
> +
> +    EXPECT_EQ(answer, agent.authenticate_request_with_parameters(params));
> +}
> +
> +
> +TEST(CachedAgent, queries_agent_if_no_cached_results_and_returns_users_answer)
> +{
> +    using namespace ::testing;
> +
> +    auto answer = throw_a_dice();
> +
> +    auto params = default_request_parameters_for_testing();
> +
> +    core::trust::Agent::RequestParameters agent_params
> +    {
> +        params.application.uid,
> +        params.application.pid,
> +        params.application.id,
> +        params.feature,
> +        params.description
> +    };
> +
> +    core::trust::Request request
> +    {
> +        params.application.id,
> +        params.feature,
> +        std::chrono::system_clock::now(),
> +        answer
> +    };
> +
> +    auto mocked_agent = a_mocked_agent();
> +    auto mocked_query = a_mocked_query();
> +    auto mocked_store = a_mocked_store();
> +
> +    ON_CALL(*mocked_agent, authenticate_request_with_parameters(agent_params))
> +            .WillByDefault(
> +                Return(
> +                    answer));
> +
> +    // We return EndOfRecord for queries, and expect the request processor
> +    // to subsequently ask the user for his answer.
> +    ON_CALL(*mocked_query, status())
> +            .WillByDefault(
> +                Return(
> +                    core::trust::Store::Query::Status::eor));
> +
> +    ON_CALL(*mocked_store, query())
> +            .WillByDefault(
> +                Return(
> +                    mocked_query));
> +
> +    EXPECT_CALL(*mocked_query, current()).Times(0);
> +    EXPECT_CALL(*mocked_store, query()).Times(1);
> +    // We expect the processor to limit the query to the respective application id
> +    // and to the respective feature.
> +    EXPECT_CALL(*mocked_query, for_application_id(params.application.id)).Times(1);
> +    EXPECT_CALL(*mocked_query, for_feature(params.feature)).Times(1);
> +    // The setup ensures that a previously stored answer is available in the store.
> +    // For that, the agent should not be queried.
> +    EXPECT_CALL(*mocked_agent, authenticate_request_with_parameters(agent_params)).Times(1);
> +
> +    core::trust::CachedAgent::Configuration configuration
> +    {
> +        mocked_agent,
> +        mocked_store
> +    };
> +
> +    core::trust::CachedAgent agent
> +    {
> +        configuration
> +    };
> +
> +    EXPECT_EQ(answer, agent.authenticate_request_with_parameters(params));
> +}
> 
> === added file 'tests/daemon_test.cpp'
> --- tests/daemon_test.cpp	1970-01-01 00:00:00 +0000
> +++ tests/daemon_test.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,195 @@
> +/*
> + * Copyright © 2013 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/daemon.h>
> +
> +#include "process_exited_successfully.h"
> +
> +#include <core/dbus/fixture.h>
> +
> +#include <core/posix/exec.h>
> +#include <core/posix/fork.h>
> +
> +#include <gmock/gmock.h>
> +
> +#include <thread>
> +
> +namespace
> +{
> +static constexpr const char* service_name
> +{
> +    "UnlikelyToEverExistOutsideOfTesting"
> +};
> +
> +static constexpr const char* endpoint
> +{
> +    "/tmp/unlikely.to.ever.exist.outside.of.testing"
> +};
> +
> +}
> +
> +TEST(Daemon, unix_domain_agents_for_stub_and_skeleton_work_as_expected)
> +{
> +    // We fire up private bus instances to ensure tests working
> +    // during package builds.
> +    core::dbus::Fixture private_buses
> +    {
> +        core::dbus::Fixture::default_session_bus_config_file(),
> +        core::dbus::Fixture::default_system_bus_config_file()
> +    };
> +
> +    std::remove(endpoint);
> +
> +    // The stub accepting trust requests, relaying them via
> +    // the configured remote agent.
> +    core::posix::ChildProcess stub = core::posix::fork([]()
> +    {
> +        const char* argv[] =
> +        {
> +            __PRETTY_FUNCTION__,
> +            "--for-service", service_name,
> +            "--remote-agent", "UnixDomainSocketRemoteAgent",
> +            "--endpoint=/tmp/unlikely.to.ever.exist.outside.of.testing"
> +        };
> +
> +        auto configuration = core::trust::Daemon::Stub::Configuration::from_command_line(6, argv);
> +
> +        return core::trust::Daemon::Stub::main(configuration);
> +    }, core::posix::StandardStream::stdin | core::posix::StandardStream::stdout);
> +
> +    // We really want to write EXPECT_TRUE(WaitForEndPointToBecomeAvailableFor(endpoint, 500ms));
> +    std::this_thread::sleep_for(std::chrono::milliseconds{500});
> +
> +    // The skeleton announcing itself to the stub instance started before via
> +    // the endpoint specified for the remote agent. In addition, it features a local
> +    // agent instance that always replies: denied.
> +    auto skeleton = core::posix::fork([]()
> +    {
> +        const char* argv[]
> +        {
> +            __PRETTY_FUNCTION__,
> +            "--remote-agent", "UnixDomainSocketRemoteAgent",
> +            "--endpoint=/tmp/unlikely.to.ever.exist.outside.of.testing",
> +            "--local-agent", "TheAlwaysDenyingLocalAgent",
> +            "--for-service", service_name,
> +            "--store-bus", "session_with_address_from_env"
> +        };
> +
> +        auto configuration = core::trust::Daemon::Skeleton::Configuration::from_command_line(10, argv);
> +
> +        return core::trust::Daemon::Skeleton::main(configuration);
> +    }, core::posix::StandardStream::empty);
> +
> +    // Wait for everything to be setup
> +    std::this_thread::sleep_for(std::chrono::milliseconds{500});
> +
> +    // And inject a request into the stub
> +    std::string answer;
> +
> +    for (int feature = 0; feature < 50; feature++)
> +    {
> +        stub.cin() << core::posix::this_process::instance().pid() << " " << ::getuid() << " " << feature << std::endl;
> +        stub.cout() >> answer;
> +    }
> +
> +    // Wait for all requests to be answered
> +    std::this_thread::sleep_for(std::chrono::milliseconds{500});
> +
> +    // Sigterm both stub and skeleton.
> +    skeleton.send_signal_or_throw(core::posix::Signal::sig_term);
> +    stub.send_signal_or_throw(core::posix::Signal::sig_term);
> +
> +    // Expect both of them to exit with success.
> +    EXPECT_TRUE(ProcessExitedSuccessfully(skeleton.wait_for(core::posix::wait::Flags::untraced)));
> +    EXPECT_TRUE(ProcessExitedSuccessfully(stub.wait_for(core::posix::wait::Flags::untraced)));
> +}
> +
> +TEST(Daemon, dbus_agents_for_stub_and_skeleton_work_as_expected)
> +{
> +    // We fire up private bus instances to ensure tests working
> +    // during package builds.
> +    core::dbus::Fixture private_buses
> +    {
> +        core::dbus::Fixture::default_session_bus_config_file(),
> +        core::dbus::Fixture::default_system_bus_config_file()
> +    };
> +
> +    static std::string bus_arg{"--bus=session_with_address_from_env"};
> +
> +    // The stub accepting trust requests, relaying them via
> +    // the configured remote agent.
> +    core::posix::ChildProcess stub = core::posix::fork([]()
> +    {
> +        const char* argv[] =
> +        {
> +            __PRETTY_FUNCTION__,
> +            "--for-service", service_name,
> +            "--remote-agent", "DBusRemoteAgent",
> +            bus_arg.c_str()
> +        };
> +
> +        auto configuration = core::trust::Daemon::Stub::Configuration::from_command_line(6, argv);
> +
> +        return core::trust::Daemon::Stub::main(configuration);
> +    }, core::posix::StandardStream::stdin | core::posix::StandardStream::stdout);
> +
> +    std::this_thread::sleep_for(std::chrono::milliseconds{500});
> +
> +    // The skeleton announcing itself to the stub instance started before via
> +    // the endpoint specified for the remote agent. In addition, it features a local
> +    // agent instance that always replies: denied.
> +    auto skeleton = core::posix::fork([]()
> +    {
> +        const char* argv[]
> +        {
> +            __PRETTY_FUNCTION__,
> +            "--remote-agent", "DBusRemoteAgent",
> +            bus_arg.c_str(),
> +            "--local-agent", "TheAlwaysDenyingLocalAgent",
> +            "--for-service", service_name,
> +            "--store-bus", "session_with_address_from_env"
> +        };
> +
> +        auto configuration = core::trust::Daemon::Skeleton::Configuration::from_command_line(10, argv);
> +
> +        return core::trust::Daemon::Skeleton::main(configuration);
> +    }, core::posix::StandardStream::empty);
> +
> +    // Wait for everything to be setup
> +    std::this_thread::sleep_for(std::chrono::milliseconds{500});
> +
> +    // And inject a request into the stub
> +    std::string answer;
> +
> +    for (int feature = 0; feature < 50; feature++)
> +    {
> +        stub.cin() << core::posix::this_process::instance().pid() << " " << ::getuid() << " " << feature << std::endl;
> +        stub.cout() >> answer;
> +    }
> +
> +    // Wait for all requests to be answered
> +    std::this_thread::sleep_for(std::chrono::milliseconds{500});
> +
> +    // Sigterm both stub and skeleton.
> +    skeleton.send_signal_or_throw(core::posix::Signal::sig_term);
> +    stub.send_signal_or_throw(core::posix::Signal::sig_term);
> +
> +    // Expect both of them to exit with success.
> +    EXPECT_TRUE(ProcessExitedSuccessfully(skeleton.wait_for(core::posix::wait::Flags::untraced)));
> +    EXPECT_TRUE(ProcessExitedSuccessfully(stub.wait_for(core::posix::wait::Flags::untraced)));
> +}
> 
> === added file 'tests/dbus_test.cpp'
> --- tests/dbus_test.cpp	1970-01-01 00:00:00 +0000
> +++ tests/dbus_test.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,425 @@
> +/*
> + * Copyright © 2013 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/trust/dbus/agent.h>
> +#include <core/trust/dbus/agent_registry.h>
> +
> +#include "mock_agent.h"
> +
> +#include <core/dbus/fixture.h>
> +#include <core/dbus/asio/executor.h>
> +
> +#include <core/posix/fork.h>
> +
> +#include <core/testing/cross_process_sync.h>
> +#include <core/testing/fork_and_run.h>
> +
> +#include <gtest/gtest.h>
> +
> +namespace
> +{
> +std::shared_ptr<core::posix::SignalTrap> a_trap_for_sig_term()
> +{
> +    auto trap = core::posix::trap_signals_for_all_subsequent_threads({core::posix::Signal::sig_term});
> +    trap->signal_raised().connect([trap](core::posix::Signal)
> +    {
> +        trap->stop();
> +    });
> +
> +    return trap;
> +}
> +
> +struct DBusAgent : public core::dbus::testing::Fixture
> +{
> +    static constexpr const char* agent_service_name
> +    {
> +        "core.trust.dbus.Agent"
> +    };
> +
> +    static constexpr const char* agent_path
> +    {
> +        "/core/trust/dbus/Agent"
> +    };
> +};
> +
> +struct DBusAgentRegistry : public core::dbus::testing::Fixture
> +{
> +    static constexpr const char* agent_registry_service_name
> +    {
> +        "core.trust.dbus.AgentRegistry"
> +    };
> +
> +    static constexpr const char* agent_registry_path
> +    {
> +        "/core/trust/dbus/AgentRegistry"
> +    };
> +};
> +
> +struct MockAgentRegistry : public core::trust::Agent::Registry
> +{
> +    // Registers an agent for the given uid.
> +    MOCK_METHOD2(register_agent_for_user, void(const core::trust::Uid&, const std::shared_ptr<core::trust::Agent>&));
> +
> +    // Removes the agent for the given uid from the registry
> +    MOCK_METHOD1(unregister_agent_for_user, void(const core::trust::Uid&));
> +};
> +
> +core::trust::Agent::RequestParameters ref_params
> +{
> +    core::trust::Uid{42},
> +    core::trust::Pid{41},
> +    "just.a.testing.id",
> +    core::trust::Feature{40},
> +    "just an example description"
> +};
> +
> +}
> +
> +TEST_F(DBusAgent, remote_invocation_works_correctly)
> +{
> +    using namespace ::testing;
> +
> +    core::testing::CrossProcessSync cps;
> +
> +    auto skeleton = [this, &cps]()
> +    {
> +        auto trap = a_trap_for_sig_term();
> +
> +        auto agent = std::make_shared<MockAgent>();
> +
> +        EXPECT_CALL(*agent, authenticate_request_with_parameters(_))
> +                .Times(1)
> +                .WillRepeatedly(Return(core::trust::Request::Answer::denied));
> +
> +        auto bus = session_bus();
> +        bus->install_executor(core::dbus::asio::make_executor(bus));
> +
> +        std::thread t{[bus]() { bus->run(); }};
> +
> +        auto service = core::dbus::Service::add_service(bus, agent_service_name);
> +        auto object = service->add_object_for_path(core::dbus::types::ObjectPath{agent_path});
> +
> +        core::trust::dbus::Agent::Skeleton skeleton
> +        {
> +            core::trust::dbus::Agent::Skeleton::Configuration
> +            {
> +                object,
> +                bus,
> +                [agent](const core::trust::Agent::RequestParameters& params)
> +                {
> +                    return agent->authenticate_request_with_parameters(params);
> +                }
> +            }
> +        };
> +
> +        cps.try_signal_ready_for(std::chrono::milliseconds{500});
> +
> +        trap->run();
> +
> +        bus->stop();
> +
> +        if (t.joinable())
> +            t.join();
> +
> +        return ::testing::Test::HasFailure() ?
> +                    core::posix::exit::Status::failure :
> +                    core::posix::exit::Status::success;
> +    };
> +
> +    auto stub = [this, &cps]()
> +    {
> +        cps.wait_for_signal_ready_for(std::chrono::milliseconds{500});
> +
> +        auto bus = session_bus();
> +        bus->install_executor(core::dbus::asio::make_executor(bus));
> +
> +        auto service = core::dbus::Service::use_service(bus, agent_service_name);
> +        auto object = service->object_for_path(core::dbus::types::ObjectPath{agent_path});
> +
> +        core::trust::dbus::Agent::Stub stub
> +        {
> +            object
> +        };
> +
> +        std::thread t{[bus]() { bus->run(); }};
> +
> +        EXPECT_EQ(core::trust::Request::Answer::denied,
> +                  stub.authenticate_request_with_parameters(ref_params));
> +
> +        bus->stop();
> +
> +        if (t.joinable())
> +            t.join();
> +
> +        return ::testing::Test::HasFailure() ?
> +                    core::posix::exit::Status::failure :
> +                    core::posix::exit::Status::success;
> +    };
> +
> +    EXPECT_EQ(core::testing::ForkAndRunResult::empty,
> +              core::testing::fork_and_run(skeleton, stub));
> +}
> +
> +TEST_F(DBusAgent, remote_invocation_throws_if_answer_non_conclusive)
> +{
> +    using namespace ::testing;
> +
> +    core::testing::CrossProcessSync cps;
> +
> +    auto skeleton = [this, &cps]()
> +    {
> +        auto trap = a_trap_for_sig_term();
> +
> +        auto agent = std::make_shared<MockAgent>();
> +
> +        EXPECT_CALL(*agent, authenticate_request_with_parameters(_))
> +                .Times(1)
> +                .WillRepeatedly(Throw(std::logic_error{""}));
> +
> +        auto bus = session_bus();
> +        bus->install_executor(core::dbus::asio::make_executor(bus));
> +
> +        std::thread t{[bus]() { bus->run(); }};
> +
> +        auto service = core::dbus::Service::add_service(bus, agent_service_name);
> +        auto object = service->add_object_for_path(core::dbus::types::ObjectPath{agent_path});
> +
> +        core::trust::dbus::Agent::Skeleton skeleton
> +        {
> +            core::trust::dbus::Agent::Skeleton::Configuration
> +            {
> +                object,
> +                bus,
> +                [agent](const core::trust::Agent::RequestParameters& params)
> +                {
> +                    return agent->authenticate_request_with_parameters(params);
> +                }
> +            }
> +        };
> +
> +        cps.try_signal_ready_for(std::chrono::milliseconds{500});
> +
> +        trap->run();
> +
> +        bus->stop();
> +
> +        if (t.joinable())
> +            t.join();
> +
> +        return ::testing::Test::HasFailure() ?
> +                    core::posix::exit::Status::failure :
> +                    core::posix::exit::Status::success;
> +    };
> +
> +    auto stub = [this, &cps]()
> +    {
> +        cps.wait_for_signal_ready_for(std::chrono::milliseconds{500});
> +
> +        auto bus = session_bus();
> +        bus->install_executor(core::dbus::asio::make_executor(bus));
> +
> +        auto service = core::dbus::Service::use_service(bus, agent_service_name);
> +        auto object = service->object_for_path(core::dbus::types::ObjectPath{agent_path});
> +
> +        core::trust::dbus::Agent::Stub stub
> +        {
> +            object
> +        };
> +
> +        std::thread t{[bus]() { bus->run(); }};
> +
> +        EXPECT_ANY_THROW(stub.authenticate_request_with_parameters(ref_params));
> +
> +        bus->stop();
> +
> +        if (t.joinable())
> +            t.join();
> +
> +        return ::testing::Test::HasFailure() ?
> +                    core::posix::exit::Status::failure :
> +                    core::posix::exit::Status::success;
> +    };
> +
> +    EXPECT_EQ(core::testing::ForkAndRunResult::empty,
> +              core::testing::fork_and_run(skeleton, stub));
> +}
> +
> +TEST_F(DBusAgentRegistry, remote_invocation_works_correctly)
> +{
> +    using namespace ::testing;
> +
> +    static const int expected_invocation_count{10};
> +
> +    core::testing::CrossProcessSync service_ready, agent_registered;
> +
> +    auto skeleton = [this, &service_ready, &agent_registered]()
> +    {
> +        auto trap = a_trap_for_sig_term();
> +
> +        core::trust::LockingAgentRegistry locking_agent_registry;
> +
> +        MockAgentRegistry agent_registry;
> +
> +        EXPECT_CALL(agent_registry, register_agent_for_user(_, _))
> +                .Times(1)
> +                .WillRepeatedly(Invoke(&locking_agent_registry, &core::trust::LockingAgentRegistry::register_agent_for_user));
> +
> +        EXPECT_CALL(agent_registry, unregister_agent_for_user(_))
> +                .Times(1);
> +
> +        auto bus = session_bus();
> +        bus->install_executor(core::dbus::asio::make_executor(bus));
> +
> +        std::thread t{[bus]() { bus->run(); }};
> +        std::thread u{[&locking_agent_registry, &agent_registered]()
> +        {
> +            agent_registered.wait_for_signal_ready_for(std::chrono::seconds{5});
> +
> +            auto agent = locking_agent_registry.agent_for_user(core::trust::Uid{::getuid()});
> +
> +            for (unsigned int i = 0; i < expected_invocation_count; i++)
> +                EXPECT_EQ(core::trust::Request::Answer::denied,
> +                          agent->authenticate_request_with_parameters(ref_params));
> +        }};
> +
> +        auto service = core::dbus::Service::add_service(bus, agent_registry_service_name);
> +        auto object = service->add_object_for_path(core::dbus::types::ObjectPath{agent_registry_path});
> +
> +        core::trust::dbus::AgentRegistry::Skeleton skeleton
> +        {
> +            core::trust::dbus::AgentRegistry::Skeleton::Configuration
> +            {
> +                object,
> +                bus,
> +                agent_registry
> +            }
> +        };
> +
> +        service_ready.try_signal_ready_for(std::chrono::milliseconds{500});
> +
> +        trap->run();
> +
> +        bus->stop();
> +
> +        if (t.joinable())
> +            t.join();
> +
> +        if (u.joinable())
> +            u.join();
> +
> +        return ::testing::Test::HasFailure() ?
> +                    core::posix::exit::Status::failure :
> +                    core::posix::exit::Status::success;
> +    };
> +
> +    auto stub = [this, &service_ready, &agent_registered]()
> +    {
> +        service_ready.wait_for_signal_ready_for(std::chrono::milliseconds{500});
> +
> +        auto bus = session_bus();
> +        bus->install_executor(core::dbus::asio::make_executor(bus));
> +
> +        auto service = core::dbus::Service::use_service(bus, agent_registry_service_name);
> +        auto agent_object = service->add_object_for_path(core::dbus::types::ObjectPath{DBusAgent::agent_path});
> +        auto agent_registry_object = service->object_for_path(core::dbus::types::ObjectPath{DBusAgentRegistry::agent_registry_path});
> +
> +        struct State
> +        {
> +            void wait()
> +            {
> +                std::unique_lock<std::mutex> ul(guard);
> +                wait_condition.wait_for(
> +                            ul,
> +                            std::chrono::milliseconds{1000},
> +                            [this]() { return invocation_count == expected_invocation_count; });
> +            }
> +
> +            void notify()
> +            {
> +                invocation_count++;
> +                wait_condition.notify_all();
> +            }
> +
> +            std::uint32_t invocation_count{0};
> +            std::mutex guard;
> +            std::condition_variable wait_condition;
> +        } state;
> +
> +        auto agent = std::make_shared<MockAgent>();
> +
> +        EXPECT_CALL(*agent, authenticate_request_with_parameters(ref_params))
> +                .Times(expected_invocation_count)
> +                .WillRepeatedly(
> +                    DoAll(
> +                        InvokeWithoutArgs(&state, &State::notify),
> +                        Return(core::trust::Request::Answer::denied)));
> +
> +        /*std::shared_ptr<core::trust::dbus::Agent::Skeleton> skeleton

No FIXME marker?

> +        {
> +            new core::trust::dbus::Agent::Skeleton
> +            {
> +                core::trust::dbus::Agent::Skeleton::Configuration
> +                {
> +                    agent_object,
> +                    bus,
> +                    [agent](const core::trust::Agent::RequestParameters& params)
> +                    {
> +                        return agent->authenticate_request_with_parameters(params);
> +                    }
> +                }
> +            }
> +        };*/
> +
> +        core::trust::dbus::AgentRegistry::Stub stub
> +        {
> +            core::trust::dbus::AgentRegistry::Stub::Configuration
> +            {
> +                agent_registry_object,
> +                core::trust::dbus::AgentRegistry::Stub::counting_object_path_generator(),
> +                service,
> +                bus
> +            }
> +        };
> +
> +        std::thread t{[bus]() { bus->run(); }};
> +
> +        // We register for the current user id.
> +        stub.register_agent_for_user(core::trust::Uid{::getuid()}, agent);
> +
> +        // Tell the other side that we are good to go.
> +        agent_registered.try_signal_ready_for(std::chrono::milliseconds{500});
> +
> +        // We wait until we have seen 5 invocations
> +        state.wait();
> +
> +        // And unregister again.
> +        stub.unregister_agent_for_user(core::trust::Uid{::getuid()});
> +
> +        bus->stop();
> +
> +        if (t.joinable())
> +            t.join();
> +
> +        return ::testing::Test::HasFailure() ?
> +                    core::posix::exit::Status::failure :
> +                    core::posix::exit::Status::success;
> +    };
> +
> +    EXPECT_EQ(core::testing::ForkAndRunResult::empty,
> +              core::testing::fork_and_run(skeleton, stub));
> +}
> 
> === modified file 'tests/mir_agent_test.cpp'
> --- tests/mir_agent_test.cpp	2014-07-17 15:37:13 +0000
> +++ tests/mir_agent_test.cpp	2014-07-31 13:42:29 +0000
> @@ -64,7 +64,7 @@
>      MOCK_METHOD3(create_prompt_session_sync,
>                   core::trust::mir::PromptSessionVirtualTable::Ptr(
>                       // The process id of the requesting app/service
> -                     pid_t app_pid,
> +                     core::trust::Pid app_pid,
>                       // Callback handling prompt session state changes.
>                       mir_prompt_session_state_change_callback,
>                       // Callback context
> @@ -99,6 +99,11 @@
>      MOCK_METHOD1(translate, core::trust::Request::Answer(const core::posix::wait::Result&));
>  };
>  
> +std::shared_ptr<MockConnectionVirtualTable> a_mocked_connection_vtable()
> +{
> +    return std::make_shared<testing::NiceMock<MockConnectionVirtualTable>>();
> +}
> +
>  std::shared_ptr<MockPromptSessionVirtualTable> a_mocked_prompt_session_vtable()
>  {
>      return std::make_shared<testing::NiceMock<MockPromptSessionVirtualTable>>();
> @@ -187,8 +192,9 @@
>  {
>      using namespace ::testing;
>  
> -    const pid_t app_pid {21};
> +    const core::trust::Pid app_pid {21};
>      const std::string app_id {"does.not.exist.application"};
> +    const core::trust::Feature feature{42};
>      const std::string app_description {"This is just an extended description"};
>      const int pre_authenticated_fd {42};
>  
> @@ -199,7 +205,7 @@
>          app_description
>      };
>  
> -    auto connection_vtable = std::make_shared<MockConnectionVirtualTable>();
> +    auto connection_vtable = a_mocked_connection_vtable();
>      auto prompt_session_vtable = a_mocked_prompt_session_vtable();
>  
>      auto prompt_provider_exec_helper = a_mocked_prompt_provider_calling_bin_false();
> @@ -215,6 +221,7 @@
>  
>      EXPECT_CALL(*connection_vtable, create_prompt_session_sync(app_pid, _, _)).Times(1);
>      EXPECT_CALL(*prompt_session_vtable, new_fd_for_prompt_provider()).Times(1);
> +
>      EXPECT_CALL(*prompt_provider_exec_helper,
>                  exec_prompt_provider_with_arguments(
>                      reference_invocation_args)).Times(1);
> @@ -227,15 +234,24 @@
>      };
>  
>      EXPECT_EQ(core::trust::Request::Answer::denied, // /bin/false exits with failure.
> -              agent.prompt_user_for_request(app_pid, app_id, app_description));
> +              agent.authenticate_request_with_parameters(
> +                  core::trust::Agent::RequestParameters
> +                  {
> +                     core::trust::Uid{::getuid()},
> +                     app_pid,
> +                     app_id,
> +                     feature,
> +                     app_description
> +                  }));
>  }
>  
>  TEST(MirAgent, sig_kills_prompt_provider_process_on_status_change)
>  {
>      using namespace ::testing;
>  
> -    const pid_t app_pid {21};
> +    const core::trust::Pid app_pid {21};
>      const std::string app_id {"does.not.exist.application"};
> +    const core::trust::Feature feature{42};
>      const std::string app_description {"This is just an extended description"};
>      const int pre_authenticated_fd {42};
>  
> @@ -305,7 +321,15 @@
>      // The spinning prompt provider should get signalled if the prompting session is stopped.
>      // If that does not happen, the prompt provider returns success and we would have a result
>      // granted.
> -    EXPECT_THROW(agent.prompt_user_for_request(app_pid, app_id, app_description),
> +    EXPECT_THROW(agent.authenticate_request_with_parameters(
> +                     core::trust::Agent::RequestParameters
> +                     {
> +                         core::trust::Uid{::getuid()},
> +                         core::trust::Pid{app_pid},
> +                         app_id,
> +                         feature,
> +                         app_description
> +                     }),
>                   std::logic_error);
>  
>      // And some clean up.
> @@ -403,7 +427,15 @@
>      auto mir_agent = core::trust::mir::create_agent_for_mir_connection(mir_connection);
>  
>      // And issue a prompt request. As a result, the user is presented with a prompting dialog.
> -    auto answer = mir_agent->prompt_user_for_request(app.pid(), "embedded prompt", "embedded prompt");
> +    auto answer = mir_agent->authenticate_request_with_parameters(
> +                core::trust::Agent::RequestParameters
> +                {
> +                    core::trust::Uid{::getuid()},
> +                    core::trust::Pid{app.pid()},
> +                    "embedded prompt",
> +                    core::trust::Feature{},
> +                    "embedded prompt"
> +                });
>  
>      // And we cross-check with the user:
>      std::cout << "You answered the trust prompt with: " << answer << "."
> 
> === added file 'tests/mock_agent.h'
> --- tests/mock_agent.h	1970-01-01 00:00:00 +0000
> +++ tests/mock_agent.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,36 @@
> +/*
> + * Copyright © 2013 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef MOCK_AGENT_H_
> +#define MOCK_AGENT_H_
> +
> +#include <core/trust/agent.h>
> +
> +#include <gmock/gmock.h>
> +
> +struct MockAgent : public core::trust::Agent
> +{
> +    /**
> +     * @brief Presents the given request to the user, returning the user-provided answer.
> +     * @param request The trust request that a user has to answer.
> +     * @param description Extended description of the trust request.
> +     */
> +    MOCK_METHOD1(authenticate_request_with_parameters, core::trust::Request::Answer(const core::trust::Agent::RequestParameters&));
> +};
> +
> +#endif // MOCK_AGENT_H_
> 
> === added file 'tests/mock_store.h'
> --- tests/mock_store.h	1970-01-01 00:00:00 +0000
> +++ tests/mock_store.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,78 @@
> +/*
> + * Copyright © 2013 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#ifndef MOCK_STORE_H_
> +#define MOCK_STORE_H_
> +
> +#include <core/trust/request.h>
> +#include <core/trust/store.h>
> +
> +#include <gmock/gmock.h>
> +
> +struct MockStore : public core::trust::Store
> +{
> +    struct MockQuery : public core::trust::Store::Query
> +    {
> +        /** @brief Access the status of the query. */
> +        MOCK_CONST_METHOD0(status, core::trust::Store::Query::Status());
> +
> +        /** @brief Limit the query to a specific application Id. */
> +        MOCK_METHOD1(for_application_id, void(const std::string&));
> +
> +        /** @brief Limit the query to a service-specific feature. */
> +        MOCK_METHOD1(for_feature, void(core::trust::Feature));
> +
> +        /** @brief Limit the query to the specified time interval. */
> +        MOCK_METHOD2(for_interval, void(const core::trust::Request::Timestamp&, const core::trust::Request::Timestamp&));
> +
> +        /** @brief Limit the query for a specific answer. */
> +        MOCK_METHOD1(for_answer, void(core::trust::Request::Answer));
> +
> +        /** @brief Query all stored requests. */
> +        MOCK_METHOD0(all, void());
> +
> +        /** @brief Execute the query against the store. */
> +        MOCK_METHOD0(execute, void());
> +
> +        /** @brief After successful execution, advance to the next request. */
> +        MOCK_METHOD0(next, void());
> +
> +        /** @brief After successful execution, erase the current element and advance to the next request. */
> +        MOCK_METHOD0(erase, void());
> +
> +        /** @brief Access the request the query currently points to. */
> +        MOCK_METHOD0(current, core::trust::Request());
> +    };
> +
> +    /** @brief Resets the state of the store, implementations should discard
> +     * all persistent and non-persistent state.
> +     */
> +    MOCK_METHOD0(reset, void());
> +
> +    /** @brief Add the provided request to the store. When this function returns true,
> +      * the request has been persisted by the implementation.
> +      */
> +    MOCK_METHOD1(add, void(const core::trust::Request&));
> +
> +    /**
> +     * @brief Create a query for this store.
> +     */
> +    MOCK_METHOD0(query, std::shared_ptr<core::trust::Store::Query>());
> +};
> +
> +#endif // MOCK_STORE_H_
> 
> === added file 'tests/process_exited_successfully.h'
> --- tests/process_exited_successfully.h	1970-01-01 00:00:00 +0000
> +++ tests/process_exited_successfully.h	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,32 @@
> +/*
> + * Copyright © 2013 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +#include <core/posix/wait.h>
> +
> +#include <gtest/gtest.h>
> +
> +::testing::AssertionResult ProcessExitedSuccessfully(const core::posix::wait::Result& result)
> +{
> +    if (core::posix::wait::Result::Status::exited != result.status)
> +        return ::testing::AssertionFailure();
> +
> +    if (core::posix::exit::Status::success != result.detail.if_exited.status)
> +        return ::testing::AssertionFailure();
> +
> +    return ::testing::AssertionSuccess();
> +}
> 
> === added file 'tests/remote_agent_test.cpp'
> --- tests/remote_agent_test.cpp	1970-01-01 00:00:00 +0000
> +++ tests/remote_agent_test.cpp	2014-07-31 13:42:29 +0000
> @@ -0,0 +1,923 @@
> +/*
> + * Copyright © 2013 Canonical Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify it
> + * under the terms of the GNU Lesser General Public License version 3,
> + * as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public License
> + * along with this program.  If not, see <http://www.gnu.org/licenses/>.
> + *
> + * Authored by: Thomas Voß <thomas.voss at canonical.com>
> + */
> +
> +// Implementation-specific header
> +#include <core/trust/remote/agent.h>
> +#include <core/trust/remote/dbus.h>
> +#include <core/trust/remote/posix.h>
> +
> +#include "mock_agent.h"
> +#include "process_exited_successfully.h"
> +
> +#include <core/posix/fork.h>
> +#include <core/testing/cross_process_sync.h>
> +
> +#include <core/dbus/asio/executor.h>
> +#include <core/dbus/fixture.h>
> +
> +#include <gmock/gmock.h>
> +#include <gtest/gtest.h>
> +
> +#include <thread>
> +
> +namespace
> +{
> +struct MockRemoteAgentStub : public core::trust::remote::Agent::Stub
> +{
> +    // Sends out the request to the receiving end, either returning an answer
> +    // or throwing an exception if no conclusive answer could be obtained from
> +    // the user.
> +    MOCK_METHOD1(send, core::trust::Request::Answer(const core::trust::Agent::RequestParameters&));
> +};
> +
> +}
> +
> +TEST(RemoteAgentStub, calls_send_for_handling_requests_and_returns_answer)
> +{
> +    using namespace ::testing;
> +
> +    core::trust::Agent::RequestParameters parameters
> +    {
> +        core::trust::Uid{21},
> +        core::trust::Pid{42},
> +        std::string{"does.not.exist"},
> +        core::trust::Feature{},
> +        std::string{"some meaningless description"}
> +    };
> +
> +    MockRemoteAgentStub stub;
> +    EXPECT_CALL(stub, send(parameters))
> +            .Times(1)
> +            .WillOnce(Return(core::trust::Request::Answer::granted));
> +
> +    EXPECT_EQ(core::trust::Request::Answer::granted,
> +              stub.authenticate_request_with_parameters(parameters));
> +}
> +
> +TEST(RemoteAgentSkeleton, calls_out_to_implementation)
> +{
> +    using namespace ::testing;
> +
> +    core::trust::Agent::RequestParameters parameters
> +    {
> +        core::trust::Uid{21},
> +        core::trust::Pid{42},
> +        std::string{"does.not.exist"},
> +        core::trust::Feature{},
> +        std::string{"some meaningless description"}
> +    };
> +
> +    std::shared_ptr<MockAgent> mock_agent
> +    {
> +        new MockAgent{}
> +    };
> +
> +    core::trust::remote::Agent::Skeleton skeleton
> +    {
> +        mock_agent
> +    };
> +
> +    EXPECT_CALL(*mock_agent, authenticate_request_with_parameters(parameters))
> +            .Times(1)
> +            .WillOnce(Return(core::trust::Request::Answer::granted));
> +
> +    EXPECT_EQ(core::trust::Request::Answer::granted,
> +              skeleton.authenticate_request_with_parameters(parameters));
> +}
> +
> +TEST(RemoteAgentStubSessionRegistry, adding_and_removing_of_a_valid_session_works)
> +{
> +    using Session = core::trust::remote::posix::Stub::Session;
> +
> +    boost::asio::io_service io_service;
> +
> +    Session::Ptr session
> +    {
> +        new Session
> +        {
> +            io_service
> +        }
> +    };
> +
> +    Session::Registry::Ptr registry
> +    {
> +        new Session::Registry{}
> +    };
> +
> +    core::trust::Uid uid{::getuid()};
> +
> +    EXPECT_NO_THROW(registry->add_session_for_uid(uid, session););
> +    EXPECT_TRUE(registry->has_session_for_uid(uid));
> +    EXPECT_NE(nullptr, registry->resolve_session_for_uid(uid));
> +    EXPECT_NO_THROW(registry->remove_session_for_uid(uid););
> +    EXPECT_FALSE(registry->has_session_for_uid(uid));
> +    EXPECT_THROW(registry->resolve_session_for_uid(uid), std::out_of_range);
> +}
> +
> +TEST(RemoteAgentStubSessionRegistry, adding_a_null_session_throws)
> +{
> +    using Session = core::trust::remote::posix::Stub::Session;
> +
> +    Session::Ptr session;
> +
> +    Session::Registry::Ptr registry
> +    {
> +        new Session::Registry{}
> +    };
> +
> +    core::trust::Uid uid{::getuid()};
> +
> +    EXPECT_THROW(registry->add_session_for_uid(uid, session), std::logic_error);
> +    EXPECT_FALSE(registry->has_session_for_uid(uid));
> +    EXPECT_THROW(registry->resolve_session_for_uid(uid), std::out_of_range);
> +}
> +
> +TEST(RemoteAgentStubSessionRegistry, resolving_a_non_existing_session_throws)
> +{
> +    using Session = core::trust::remote::posix::Stub::Session;
> +
> +    Session::Registry::Ptr registry
> +    {
> +        new Session::Registry{}
> +    };
> +
> +    core::trust::Uid uid{::getuid()};
> +
> +    EXPECT_THROW(registry->resolve_session_for_uid(uid), std::out_of_range);
> +}
> +
> +TEST(RemoteAgentStubSessionRegistry, removing_a_non_existing_session_does_not_throw)
> +{
> +    using Session = core::trust::remote::posix::Stub::Session;
> +
> +    Session::Registry::Ptr registry
> +    {
> +        new Session::Registry{}
> +    };
> +
> +    core::trust::Uid uid{::getuid()};
> +
> +    EXPECT_FALSE(registry->has_session_for_uid(uid));
> +    EXPECT_NO_THROW(registry->remove_session_for_uid(uid));
> +}
> +
> +
> +namespace
> +{
> +struct UnixDomainSocketRemoteAgent : public ::testing::Test
> +{
> +    static constexpr const char* endpoint_for_testing
> +    {
> +        "/tmp/unlikely.to.ever.be.used.by.someone.else"
> +    };
> +
> +    UnixDomainSocketRemoteAgent()
> +        : keep_alive{io_service},
> +          worker{[this]() { io_service.run(); }}
> +    {
> +        std::remove(endpoint_for_testing);
> +    }
> +
> +    ~UnixDomainSocketRemoteAgent()
> +    {
> +        io_service.stop();
> +
> +        if (worker.joinable())
> +            worker.join();
> +    }
> +
> +    // Returns the default stub setup for testing
> +    core::trust::remote::posix::Stub::Configuration the_default_stub_configuration()
> +    {
> +        return core::trust::remote::posix::Stub::Configuration
> +        {
> +            io_service,
> +            boost::asio::local::stream_protocol::endpoint{UnixDomainSocketRemoteAgent::endpoint_for_testing},
> +            core::trust::remote::helpers::proc_stat_start_time_resolver(),
> +            core::trust::remote::posix::Stub::get_sock_opt_credentials_resolver(),
> +            std::make_shared<core::trust::remote::posix::Stub::Session::Registry>()
> +        };
> +    }
> +
> +    // Returns a peer ready to be forked that connects to the endpoint_for_testing
> +    // and immediately exits, checking for any sort of testing failures that might have
> +    // occured in between.
> +    static std::function<core::posix::exit::Status()> a_raw_peer_immediately_exiting()
> +    {
> +        return []()
> +        {
> +            boost::asio::io_service io_service;
> +            boost::asio::io_service::work keep_alive{io_service};
> +
> +            std::thread worker{[&io_service]() { io_service.run(); }};
> +
> +            boost::asio::local::stream_protocol::socket socket{io_service};
> +
> +            EXPECT_NO_THROW(socket.connect(boost::asio::local::stream_protocol::endpoint{UnixDomainSocketRemoteAgent::endpoint_for_testing}));
> +
> +            io_service.stop();
> +            if(worker.joinable())
> +                worker.join();
> +
> +            return ::testing::Test::HasFailure() ?
> +                        core::posix::exit::Status::failure :
> +                        core::posix::exit::Status::success;
> +        };
> +    }
> +
> +
> +
> +    boost::asio::io_service io_service;
> +    boost::asio::io_service::work keep_alive;
> +
> +    std::thread worker;
> +};
> +
> +struct MockProcessStartTimeResolver
> +{
> +    virtual ~MockProcessStartTimeResolver() = default;
> +
> +    core::trust::remote::helpers::ProcessStartTimeResolver to_functional()
> +    {
> +        return [this](core::trust::Pid pid)
> +        {
> +            return resolve_process_start_time(pid);
> +        };
> +    }
> +
> +    MOCK_METHOD1(resolve_process_start_time, std::int64_t(core::trust::Pid));
> +};
> +
> +}
> +
> +TEST_F(UnixDomainSocketRemoteAgent, accepts_connections_on_endpoint)
> +{
> +    auto stub = core::trust::remote::posix::Stub::create_stub_for_configuration(
> +                the_default_stub_configuration());
> +
> +    core::posix::ChildProcess child = core::posix::fork(
> +                UnixDomainSocketRemoteAgent::a_raw_peer_immediately_exiting(),
> +                core::posix::StandardStream::empty);
> +
> +    EXPECT_TRUE(ProcessExitedSuccessfully(child.wait_for(core::posix::wait::Flags::untraced)));
> +    EXPECT_TRUE(stub->has_session_for_uid(core::trust::Uid{::getuid()}));
> +}
> +
> +TEST_F(UnixDomainSocketRemoteAgent, queries_peer_credentials_for_incoming_connection)
> +{
> +    static const core::trust::Uid uid{42};
> +    bool peer_credentials_queried{false};
> +
> +    auto config = the_default_stub_configuration();
> +
> +    config.peer_credentials_resolver = [&peer_credentials_queried](int)
> +    {
> +        peer_credentials_queried = true;
> +        return std::make_tuple(uid, core::trust::Pid{42}, core::trust::Gid{42});
> +    };
> +
> +    auto stub = core::trust::remote::posix::Stub::create_stub_for_configuration(config);
> +
> +    core::posix::ChildProcess child = core::posix::fork(
> +                UnixDomainSocketRemoteAgent::a_raw_peer_immediately_exiting(),
> +                core::posix::StandardStream::empty);
> +
> +    EXPECT_TRUE(ProcessExitedSuccessfully(child.wait_for(core::posix::wait::Flags::untraced)));
> +
> +    EXPECT_TRUE(peer_credentials_queried);
> +    EXPECT_TRUE(stub->has_session_for_uid(uid));
> +}
> +
> +TEST_F(UnixDomainSocketRemoteAgent, stub_and_skeleton_query_process_start_time_for_request)
> +{
> +    using namespace ::testing;
> +
> +    // We need to make sure that we have a valid pid.
> +    auto app = core::posix::fork([]()
> +    {
> +        while(true) std::this_thread::sleep_for(std::chrono::milliseconds{500});
> +        return core::posix::exit::Status::success;
> +    }, core::posix::StandardStream::empty);
> +
> +    // skeleton --| good to go |--> stub
> +    core::testing::CrossProcessSync cps;
> +
> +    NiceMock<MockProcessStartTimeResolver> process_start_time_resolver;
> +
> +    auto config = the_default_stub_configuration();
> +    config.start_time_resolver = process_start_time_resolver.to_functional();
> +
> +    auto stub = core::trust::remote::posix::Stub::create_stub_for_configuration(config);
> +
> +    core::posix::ChildProcess child = core::posix::fork([&cps]()
> +    {
> +        auto trap = core::posix::trap_signals_for_all_subsequent_threads({core::posix::Signal::sig_term});
> +
> +        trap->signal_raised().connect([trap](core::posix::Signal)
> +        {
> +            trap->stop();
> +        });
> +
> +        boost::asio::io_service io_service;
> +        boost::asio::io_service::work work{io_service};
> +        std::thread worker{[&io_service] { io_service.run(); }};
> +
> +        NiceMock<MockProcessStartTimeResolver> process_start_time_resolver;
> +
> +        EXPECT_CALL(process_start_time_resolver, resolve_process_start_time(_))
> +                .Times(1)
> +                .WillRepeatedly(Return(42));
> +
> +        auto mock_agent = std::make_shared<::testing::NiceMock<MockAgent>>();
> +
> +        EXPECT_CALL(*mock_agent, authenticate_request_with_parameters(_))
> +                .Times(1)
> +                .WillRepeatedly(Return(core::trust::Request::Answer::denied));
> +
> +        core::trust::remote::posix::Skeleton::Configuration config
> +        {
> +            mock_agent,
> +            io_service,
> +            boost::asio::local::stream_protocol::endpoint{UnixDomainSocketRemoteAgent::endpoint_for_testing},
> +            process_start_time_resolver.to_functional(),
> +            core::trust::remote::helpers::aa_get_task_con_app_id_resolver(),
> +            "Just a test for %1%."
> +        };
> +
> +        auto skeleton = core::trust::remote::posix::Skeleton::create_skeleton_for_configuration(config);
> +
> +        cps.try_signal_ready_for(std::chrono::milliseconds{500});
> +
> +        trap->run();
> +
> +        io_service.stop();
> +
> +        if (worker.joinable())
> +            worker.join();
> +
> +        return ::testing::Test::HasFailure() ?
> +                    core::posix::exit::Status::failure :
> +                    core::posix::exit::Status::success;
> +    }, core::posix::StandardStream::empty);
> +
> +    cps.wait_for_signal_ready_for(std::chrono::milliseconds{500});
> +
> +    EXPECT_CALL(process_start_time_resolver, resolve_process_start_time(_))
> +            .Times(2)
> +            .WillRepeatedly(Return(42));
> +
> +    EXPECT_EQ(core::trust::Request::Answer::denied,
> +              stub->authenticate_request_with_parameters(
> +                  core::trust::Agent::RequestParameters
> +                  {
> +                      core::trust::Uid{::getuid()},
> +                      core::trust::Pid{app.pid()},
> +                      "",
> +                      core::trust::Feature{},
> +                      ""
> +                  }));
> +
> +    child.send_signal_or_throw(core::posix::Signal::sig_term);
> +    EXPECT_TRUE(ProcessExitedSuccessfully(child.wait_for(core::posix::wait::Flags::untraced)));
> +}
> +
> +/**************************************
> +  Full blown acceptance tests go here.
> +**************************************/
> +
> +#include <ctime>
> +
> +namespace
> +{
> +static constexpr const char* endpoint_for_acceptance_testing
> +{
> +    "/tmp/endpoint.for.acceptance.testing"
> +};
> +
> +// Our rng used in acceptance testing, seeded with the current time since
> +// the epoch in seconds.
> +static std::default_random_engine rng
> +{
> +    static_cast<std::default_random_engine::result_type>(std::time(nullptr))
> +};
> +
> +// Our dice
> +static std::uniform_int_distribution<int> dice(1,6);
> +}
> +
> +TEST(UnixDomainSocket, a_service_can_query_a_remote_agent)
> +{
> +    using namespace ::testing;
> +
> +    std::remove(endpoint_for_acceptance_testing);
> +
> +    core::testing::CrossProcessSync
> +            stub_ready,         // signals stub     --| I'm ready |--> skeleton
> +            skeleton_ready;     // signals skeleton --| I'm ready |--> stub
> +
> +    auto app = core::posix::fork([]()
> +    {
> +        while(true) std::this_thread::sleep_for(std::chrono::milliseconds{500});
> +        return core::posix::exit::Status::success;
> +    }, core::posix::StandardStream::empty);
> +
> +    // We sample an answer by throwing a dice.
> +    const core::trust::Request::Answer answer
> +    {
> +        dice(rng) <= 3 ?
> +                    core::trust::Request::Answer::denied :
> +                    core::trust::Request::Answer::granted
> +    };
> +
> +    const core::trust::Uid app_uid{::getuid()};
> +    const core::trust::Pid app_pid{app.pid()};
> +
> +    auto stub = core::posix::fork([app_uid, app_pid, answer, &stub_ready, &skeleton_ready]()
> +    {
> +        boost::asio::io_service io_service;
> +        boost::asio::io_service::work keep_alive{io_service};
> +
> +        std::thread worker{[&io_service]() { io_service.run(); }};
> +
> +        core::trust::remote::posix::Stub::Configuration config
> +        {
> +            io_service,
> +            boost::asio::local::stream_protocol::endpoint{endpoint_for_acceptance_testing},
> +            core::trust::remote::helpers::proc_stat_start_time_resolver(),
> +            core::trust::remote::posix::Stub::get_sock_opt_credentials_resolver(),
> +            std::make_shared<core::trust::remote::posix::Stub::Session::Registry>()
> +        };
> +
> +        auto stub = core::trust::remote::posix::Stub::create_stub_for_configuration(config);
> +
> +        stub_ready.try_signal_ready_for(std::chrono::milliseconds{1000});
> +        skeleton_ready.wait_for_signal_ready_for(std::chrono::milliseconds{1000});
> +
> +        for (unsigned int i = 0; i < 100; i++)
> +        {
> +            EXPECT_EQ(answer, stub->authenticate_request_with_parameters(
> +                          core::trust::Agent::RequestParameters
> +                          {
> +                              app_uid,
> +                              app_pid,
> +                              "",
> +                              core::trust::Feature{},
> +                              ""
> +                          }));
> +        }
> +
> +        io_service.stop();
> +
> +        if (worker.joinable())
> +            worker.join();
> +
> +        return Test::HasFailure() ?
> +                    core::posix::exit::Status::failure :
> +                    core::posix::exit::Status::success;
> +    }, core::posix::StandardStream::empty);
> +
> +    auto skeleton = core::posix::fork([answer, &stub_ready, &skeleton_ready]()
> +    {
> +        auto trap = core::posix::trap_signals_for_all_subsequent_threads({core::posix::Signal::sig_term});
> +
> +        trap->signal_raised().connect([trap](core::posix::Signal)
> +        {
> +            trap->stop();
> +        });
> +
> +        boost::asio::io_service io_service;
> +        boost::asio::io_service::work keep_alive{io_service};
> +
> +        std::thread worker{[&io_service]() { io_service.run(); }};
> +
> +        // We have to rely on a MockAgent to break the dependency on a running Mir instance.
> +        auto mock_agent = std::make_shared<::testing::NiceMock<MockAgent>>();
> +
> +        ON_CALL(*mock_agent, authenticate_request_with_parameters(_))
> +                .WillByDefault(Return(answer));
> +
> +        core::trust::remote::posix::Skeleton::Configuration config
> +        {
> +            mock_agent,
> +            io_service,
> +            boost::asio::local::stream_protocol::endpoint{endpoint_for_acceptance_testing},
> +            core::trust::remote::helpers::proc_stat_start_time_resolver(),
> +            core::trust::remote::helpers::aa_get_task_con_app_id_resolver(),
> +            "Just a test for %1%."
> +        };
> +
> +        stub_ready.wait_for_signal_ready_for(std::chrono::milliseconds{1000});
> +        auto skeleton = core::trust::remote::posix::Skeleton::create_skeleton_for_configuration(config);
> +        skeleton_ready.try_signal_ready_for(std::chrono::milliseconds{1000});
> +
> +        trap->run();
> +
> +        io_service.stop();
> +
> +        if (worker.joinable())
> +            worker.join();
> +
> +        return core::posix::exit::Status::success;
> +    }, core::posix::StandardStream::empty);
> +
> +    EXPECT_TRUE(ProcessExitedSuccessfully(stub.wait_for(core::posix::wait::Flags::untraced)));
> +    skeleton.send_signal_or_throw(core::posix::Signal::sig_term);
> +    EXPECT_TRUE(ProcessExitedSuccessfully(skeleton.wait_for(core::posix::wait::Flags::untraced)));
> +
> +    app.send_signal_or_throw(core::posix::Signal::sig_kill);
> +}
> +
> +// A test case using a standalone service that is not using our header files.
> +// The test-case here is equivalent to the code that we are wiring up in the
> +// Android camera service.
> +
> +#include <sys/socket.h>
> +#include <sys/un.h>
> +
> +namespace
> +{
> +// We declare another struct for testing purposes.
> +struct Request
> +{
> +    uid_t uid; pid_t pid; ::uint64_t feature; ::int64_t start_time;
> +};
> +}
> +
> +TEST(UnixDomainSocket, a_standalone_service_can_query_a_remote_agent)
> +{
> +    using namespace ::testing;
> +
> +    std::remove(endpoint_for_acceptance_testing);
> +
> +    core::testing::CrossProcessSync
> +            stub_ready,         // signals stub     --| I'm ready |--> skeleton
> +            skeleton_ready;     // signals skeleton --| I'm ready |--> stub
> +
> +    auto app = core::posix::fork([]()
> +    {
> +        while(true) std::this_thread::sleep_for(std::chrono::milliseconds{500});
> +        return core::posix::exit::Status::success;
> +    }, core::posix::StandardStream::empty);
> +
> +    // We sample an answer by throwing a dice.
> +    const core::trust::Request::Answer answer
> +    {
> +        dice(rng) <= 3 ?
> +                    core::trust::Request::Answer::denied :
> +                    core::trust::Request::Answer::granted
> +    };
> +
> +    const core::trust::Uid app_uid{::getuid()};
> +    const core::trust::Pid app_pid{app.pid()};
> +
> +    auto start_time_helper = core::trust::remote::helpers::proc_stat_start_time_resolver();
> +    auto app_start_time = start_time_helper(core::trust::Pid{app.pid()});
> +
> +    auto stub = core::posix::fork([app_uid, app_pid, app_start_time, answer, &stub_ready, &skeleton_ready]()
> +    {
> +        // Stores all known connections indexed by user id
> +        std::map<uid_t, int> known_connections_by_user_id;
> +
> +        static const int socket_error_code{-1};
> +
> +        // We create a unix domain socket
> +        int socket_fd = ::socket(PF_UNIX, SOCK_STREAM, 0);
> +
> +        if (socket_fd == socket_error_code)
> +        {
> +            ::perror("Could not create unix stream socket");
> +            return core::posix::exit::Status::failure;
> +        }
> +
> +        // Prepare for binding to endpoint in file system.
> +        // Consciously ignoring errors here.
> +        ::unlink(endpoint_for_acceptance_testing);
> +
> +        // Setup address
> +        sockaddr_un address;
> +        ::memset(&address, 0, sizeof(sockaddr_un));
> +
> +        address.sun_family = AF_UNIX;
> +        ::snprintf(address.sun_path, 108, endpoint_for_acceptance_testing);
> +
> +        // And bind to the endpoint in the filesystem.
> +        static const int bind_error_code{-1};
> +        int rc = ::bind(socket_fd, reinterpret_cast<sockaddr*>(&address), sizeof(sockaddr_un));
> +
> +        if (rc == bind_error_code)
> +        {
> +            ::perror("Could not bind to endpoint");
> +            return core::posix::exit::Status::failure;
> +        }
> +
> +        // Start listening for incoming connections.
> +        static const int listen_error_code{-1};
> +        static const int back_log_size{5};
> +
> +        rc = ::listen(socket_fd, back_log_size);
> +
> +        if (rc == listen_error_code)
> +        {
> +            ::perror("Could not start listening for incoming connections");
> +            return core::posix::exit::Status::failure;
> +        }
> +
> +        // Spawn a thread for accepting connections.
> +        std::thread acceptor([socket_fd, &known_connections_by_user_id]()
> +        {
> +            bool keep_on_running{true};
> +            // Error code when accepting connections.
> +            static const int accept_error_code{-1};
> +
> +            // Error code when querying socket options.
> +            static const int get_sock_opt_error = -1;
> +            // Some state we preserve across loop iterations.
> +            sockaddr_un address;
> +            socklen_t address_length = sizeof(sockaddr_un);
> +
> +            ucred peer_credentials { 0, 0, 0 };
> +            socklen_t len = sizeof(peer_credentials);
> +
> +            while(keep_on_running)
> +            {
> +                address_length = sizeof(sockaddr_un);
> +                int connection_fd = ::accept(socket_fd, reinterpret_cast<sockaddr*>(&address), &address_length);
> +
> +                if (connection_fd == accept_error_code)
> +                {
> +                    switch (errno)
> +                    {
> +                    case EINTR:
> +                    case EAGAIN:
> +                        keep_on_running = true;
> +                        break;
> +                    default:
> +                        keep_on_running = false;
> +                        break;
> +                    }
> +                } else
> +                {
> +                    // We query the peer credentials
> +                    len = sizeof(ucred);
> +                    auto rc = ::getsockopt(connection_fd, SOL_SOCKET, SO_PEERCRED, &peer_credentials, &len);
> +
> +                    if (rc == get_sock_opt_error)
> +                    {
> +                        ::perror("Could not query peer credentials");
> +                    } else
> +                    {
> +                        known_connections_by_user_id.insert(
> +                                    std::make_pair(
> +                                        peer_credentials.uid,
> +                                        connection_fd));
> +                    }
> +                }
> +            }
> +        });
> +
> +        // This is obviously super ugly, but we would like to avoid implementing
> +        // a full blown reactor pattern.
> +        acceptor.detach();
> +
> +        stub_ready.try_signal_ready_for(std::chrono::milliseconds{1000});
> +        skeleton_ready.wait_for_signal_ready_for(std::chrono::milliseconds{1000});
> +
> +        std::this_thread::sleep_for(std::chrono::milliseconds{500});
> +
> +        Request request
> +        {
> +            app_uid.value,
> +            app_pid.value,
> +            0,
> +            app_start_time
> +        };
> +
> +        for (unsigned int i = 0; i < 100; i++)
> +        {
> +            std::int32_t answer_from_socket;
> +
> +            int socket = known_connections_by_user_id.at(::getuid());
> +
> +            EXPECT_NE(-1, ::write(socket, &request, sizeof(Request)));
> +            EXPECT_NE(-1, ::read(socket, &answer_from_socket, sizeof(std::int32_t)));
> +
> +            EXPECT_EQ(static_cast<std::int32_t>(answer), answer_from_socket);
> +        }
> +
> +        ::close(socket_fd);
> +
> +        return Test::HasFailure() ?
> +                    core::posix::exit::Status::failure :
> +                    core::posix::exit::Status::success;
> +    }, core::posix::StandardStream::empty);
> +
> +    auto skeleton = core::posix::fork([answer, &stub_ready, &skeleton_ready]()
> +    {
> +        auto trap = core::posix::trap_signals_for_all_subsequent_threads({core::posix::Signal::sig_term});
> +
> +        trap->signal_raised().connect([trap](core::posix::Signal)
> +        {
> +            trap->stop();
> +        });
> +
> +        boost::asio::io_service io_service;
> +        boost::asio::io_service::work keep_alive{io_service};
> +
> +        std::thread worker{[&io_service]() { io_service.run(); }};
> +
> +        // We have to rely on a MockAgent to break the dependency on a running Mir instance.
> +        auto mock_agent = std::make_shared<::testing::NiceMock<MockAgent>>();
> +
> +        ON_CALL(*mock_agent, authenticate_request_with_parameters(_))
> +                .WillByDefault(Return(answer));
> +
> +        core::trust::remote::posix::Skeleton::Configuration config
> +        {
> +            mock_agent,
> +            io_service,
> +            boost::asio::local::stream_protocol::endpoint{endpoint_for_acceptance_testing},
> +            core::trust::remote::helpers::proc_stat_start_time_resolver(),
> +            core::trust::remote::helpers::aa_get_task_con_app_id_resolver(),
> +            "Just a test for %1%."
> +        };
> +
> +        stub_ready.wait_for_signal_ready_for(std::chrono::milliseconds{1000});
> +        auto skeleton = core::trust::remote::posix::Skeleton::create_skeleton_for_configuration(config);
> +        skeleton_ready.try_signal_ready_for(std::chrono::milliseconds{1000});
> +
> +        trap->run();
> +
> +        io_service.stop();
> +
> +        if (worker.joinable())
> +            worker.join();
> +
> +        return core::posix::exit::Status::success;
> +    }, core::posix::StandardStream::empty);
> +
> +    EXPECT_TRUE(ProcessExitedSuccessfully(stub.wait_for(core::posix::wait::Flags::untraced)));
> +    skeleton.send_signal_or_throw(core::posix::Signal::sig_term);
> +    EXPECT_TRUE(ProcessExitedSuccessfully(skeleton.wait_for(core::posix::wait::Flags::untraced)));
> +
> +    app.send_signal_or_throw(core::posix::Signal::sig_kill);
> +}
> +
> +namespace
> +{
> +struct DBus : public core::dbus::testing::Fixture
> +{
> +};
> +
> +std::string service_name
> +{
> +    "JustForTestingPurposes"
> +};
> +}
> +// A test-case using the remote dbus agent implementation.
> +TEST_F(DBus, a_service_can_query_a_remote_agent)
> +{
> +    using namespace ::testing;
> +
> +    core::testing::CrossProcessSync
> +            stub_ready,         // signals stub     --| I'm ready |--> skeleton
> +            skeleton_ready;     // signals skeleton --| I'm ready |--> stub
> +
> +    auto app = core::posix::fork([]()
> +    {
> +        while(true) std::this_thread::sleep_for(std::chrono::milliseconds{500});
> +        return core::posix::exit::Status::success;
> +    }, core::posix::StandardStream::empty);
> +
> +    // We sample an answer by throwing a dice.
> +    const core::trust::Request::Answer answer
> +    {
> +        dice(rng) <= 3 ?
> +                    core::trust::Request::Answer::denied :
> +                    core::trust::Request::Answer::granted
> +    };
> +
> +    const core::trust::Uid app_uid{::getuid()};
> +    const core::trust::Pid app_pid{app.pid()};
> +
> +    auto stub = core::posix::fork([this, app_uid, app_pid, answer, &stub_ready, &skeleton_ready]()
> +    {
> +        core::trust::Agent::RequestParameters ref_params
> +        {
> +            app_uid,
> +            app_pid,
> +            "just.a.testing.id",
> +            core::trust::Feature{40},
> +            "just an example description"
> +        };
> +
> +        auto bus = session_bus();
> +        bus->install_executor(core::dbus::asio::make_executor(bus));
> +
> +        std::thread worker{[bus]() { bus->run(); }};
> +
> +        std::string dbus_service_name = core::trust::remote::dbus::default_service_name_prefix + std::string{"."} + service_name;
> +
> +        auto service = core::dbus::Service::add_service(bus, dbus_service_name);
> +        auto object = service->add_object_for_path(core::dbus::types::ObjectPath
> +        {
> +            core::trust::remote::dbus::default_agent_registry_path
> +        });
> +
> +        core::trust::remote::dbus::Agent::Stub::Configuration config
> +        {
> +            object,
> +            bus
> +        };
> +
> +        auto stub = std::make_shared<core::trust::remote::dbus::Agent::Stub>(config);
> +
> +        stub_ready.try_signal_ready_for(std::chrono::milliseconds{1000});
> +        skeleton_ready.wait_for_signal_ready_for(std::chrono::milliseconds{1000});
> +
> +        for (unsigned int i = 0; i < 100; i++)
> +            EXPECT_EQ(answer, stub->authenticate_request_with_parameters(ref_params));
> +
> +        bus->stop();
> +
> +        if (worker.joinable())
> +            worker.join();
> +
> +        return Test::HasFailure() ?
> +                    core::posix::exit::Status::failure :
> +                    core::posix::exit::Status::success;
> +    }, core::posix::StandardStream::empty);
> +
> +    auto skeleton = core::posix::fork([this, answer, &stub_ready, &skeleton_ready]()
> +    {
> +        auto trap = core::posix::trap_signals_for_all_subsequent_threads({core::posix::Signal::sig_term});
> +
> +        trap->signal_raised().connect([trap](core::posix::Signal)
> +        {
> +            trap->stop();
> +        });
> +
> +        auto bus = session_bus();
> +        bus->install_executor(core::dbus::asio::make_executor(bus));
> +
> +        std::thread worker{[bus]() { bus->run(); }};
> +
> +        // We have to rely on a MockAgent to break the dependency on a running Mir instance.
> +        auto mock_agent = std::make_shared<::testing::NiceMock<MockAgent>>();
> +
> +        ON_CALL(*mock_agent, authenticate_request_with_parameters(_))
> +                .WillByDefault(Return(answer));
> +
> +        std::string dbus_service_name = core::trust::remote::dbus::default_service_name_prefix + std::string{"."} + service_name;
> +
> +        stub_ready.wait_for_signal_ready_for(std::chrono::milliseconds{1000});
> +
> +        auto service = core::dbus::Service::use_service(bus, dbus_service_name);
> +        auto object = service->object_for_path(core::dbus::types::ObjectPath
> +        {
> +            core::trust::remote::dbus::default_agent_registry_path
> +        });
> +
> +        core::trust::remote::dbus::Agent::Skeleton::Configuration config
> +        {
> +            mock_agent,
> +            object,
> +            service,
> +            bus,
> +            core::trust::remote::helpers::aa_get_task_con_app_id_resolver()
> +        };
> +
> +        auto skeleton = std::make_shared<core::trust::remote::dbus::Agent::Skeleton>(config);
> +
> +        skeleton_ready.try_signal_ready_for(std::chrono::milliseconds{1000});
> +
> +        trap->run();
> +
> +        bus->stop();
> +
> +        if (worker.joinable())
> +            worker.join();
> +
> +        return core::posix::exit::Status::success;
> +    }, core::posix::StandardStream::empty);
> +
> +    EXPECT_TRUE(ProcessExitedSuccessfully(stub.wait_for(core::posix::wait::Flags::untraced)));
> +    skeleton.send_signal_or_throw(core::posix::Signal::sig_term);
> +    EXPECT_TRUE(ProcessExitedSuccessfully(skeleton.wait_for(core::posix::wait::Flags::untraced)));
> +
> +    app.send_signal_or_throw(core::posix::Signal::sig_kill);
> +}
> 
> === modified file 'tests/remote_trust_store_test.cpp'
> --- tests/remote_trust_store_test.cpp	2014-07-16 21:34:03 +0000
> +++ tests/remote_trust_store_test.cpp	2014-07-31 13:42:29 +0000
> @@ -39,6 +39,12 @@
>  
>  struct RemoteTrustStore : public core::dbus::testing::Fixture
>  {
> +    RemoteTrustStore()
> +    {
> +        // The tests being executed under this fixture are quite intense given their accesses
> +        // to the disk. For that, we choose sufficiently high default timeout for the daemons.
> +        core::dbus::Fixture::default_daemon_timeout() = core::dbus::Fixture::Seconds{300};
> +    }
>  };
>  }
>  
> @@ -93,7 +99,7 @@
>      core::trust::Request prototype_request
>      {
>          "com.does.not.exist.app1",
> -        0,
> +        core::trust::Feature{0},
>          std::chrono::system_clock::time_point{std::chrono::seconds(500)},
>          core::trust::Request::Answer::granted
>      };
> @@ -139,12 +145,12 @@
>  
>          for (unsigned int i = 0; i < request_count; i++)
>          {
> -            r.feature = i;
> +            r.feature.value = i;
>              store->add(r);
>          }
>  
>          // Resetting the feature counter and checking if all requests have been stored.
> -        r.feature = 0;
> +        r.feature.value = 0;
>          auto query = store->query();
>          query->execute();
>          EXPECT_EQ(core::trust::Store::Query::Status::has_more_results, query->status());
> @@ -152,7 +158,7 @@
>          {
>              EXPECT_EQ(r, query->current());
>              query->next();
> -            r.feature++;
> +            r.feature.value++;
>          }
>  
>          return ::testing::Test::HasFatalFailure() || ::testing::Test::HasFailure() ?
> @@ -207,7 +213,7 @@
>          core::trust::Request r1
>          {
>              app1,
> -            0,
> +            core::trust::Feature{0},
>              std::chrono::system_clock::now(),
>              core::trust::Request::Answer::granted
>          };
> @@ -215,7 +221,7 @@
>          core::trust::Request r2
>          {
>              app2,
> -            1,
> +            core::trust::Feature{1},
>              std::chrono::system_clock::now(),
>              core::trust::Request::Answer::granted
>          };
> @@ -275,7 +281,7 @@
>          core::trust::Request r1
>          {
>              app1,
> -            0,
> +            core::trust::Feature{0},
>              std::chrono::system_clock::now(),
>              core::trust::Request::Answer::granted
>          };
> @@ -283,7 +289,7 @@
>          core::trust::Request r2
>          {
>              app1,
> -            1,
> +            core::trust::Feature{1},
>              std::chrono::system_clock::now(),
>              core::trust::Request::Answer::granted
>          };
> @@ -343,7 +349,7 @@
>          core::trust::Request r1
>          {
>              app1,
> -            0,
> +            core::trust::Feature{0},
>              std::chrono::system_clock::now(),
>              core::trust::Request::Answer::granted
>          };
> @@ -351,7 +357,7 @@
>          core::trust::Request r2
>          {
>              app1,
> -            1,
> +            core::trust::Feature{1},
>              std::chrono::system_clock::now(),
>              core::trust::Request::Answer::denied
>          };
> @@ -411,7 +417,7 @@
>          core::trust::Request r1
>          {
>              app1,
> -            0,
> +            core::trust::Feature{0},
>              std::chrono::system_clock::time_point(std::chrono::seconds{0}),
>              core::trust::Request::Answer::granted
>          };
> @@ -419,7 +425,7 @@
>          core::trust::Request r2
>          {
>              app1,
> -            1,
> +            core::trust::Feature{1},
>              std::chrono::system_clock::time_point(std::chrono::seconds{500}),
>              core::trust::Request::Answer::granted
>          };
> @@ -427,7 +433,7 @@
>          core::trust::Request r3
>          {
>              app1,
> -            1,
> +            core::trust::Feature{1},
>              std::chrono::system_clock::now(),
>              core::trust::Request::Answer::granted
>          };
> @@ -492,7 +498,7 @@
>          core::trust::Request r1
>          {
>              app1,
> -            0,
> +            core::trust::Feature{0},
>              std::chrono::system_clock::time_point(std::chrono::seconds{0}),
>              core::trust::Request::Answer::granted
>          };
> @@ -500,7 +506,7 @@
>          core::trust::Request r2
>          {
>              app1,
> -            1,
> +            core::trust::Feature{1},
>              std::chrono::system_clock::time_point(std::chrono::seconds{500}),
>              core::trust::Request::Answer::granted
>          };
> @@ -508,7 +514,7 @@
>          core::trust::Request r3
>          {
>              app1,
> -            1,
> +            core::trust::Feature{1},
>              std::chrono::system_clock::now(),
>              core::trust::Request::Answer::denied
>          };
> @@ -575,14 +581,14 @@
>              core::trust::Request r
>              {
>                  "this.does.not.exist.app",
> -                base_feature,
> +                core::trust::Feature{base_feature},
>                  std::chrono::system_clock::now(),
>                  core::trust::Request::Answer::granted
>              };
>  
>              for (unsigned int i = 0; i < 100; i++)
>              {
> -                r.feature = base + i;
> +                r.feature.value = base + i;
>                  try
>                  {
>                      store->add(r);
> @@ -658,14 +664,14 @@
>              core::trust::Request r
>              {
>                  "this.does.not.exist.app",
> -                0,
> +                core::trust::Feature{0},
>                  std::chrono::system_clock::now(),
>                  core::trust::Request::Answer::granted
>              };
>  
>              for (unsigned int i = 0; i < 100; i++)
>              {
> -                r.feature = i;
> +                r.feature.value = i;
>                  store->add(r);
>              }
>  
> 
> === modified file 'tests/request_processor_test.cpp'
> --- tests/request_processor_test.cpp	2014-07-18 15:25:31 +0000
> +++ tests/request_processor_test.cpp	2014-07-31 13:42:29 +0000
> @@ -20,6 +20,9 @@
>  #include <core/trust/request.h>
>  #include <core/trust/store.h>
>  
> +#include "mock_agent.h"
> +#include "mock_store.h"
> +
>  #include <gmock/gmock.h>
>  #include <gtest/gtest.h>
>  
> @@ -27,75 +30,19 @@
>  
>  namespace
>  {
> -struct MockAgent : public core::trust::Agent
> -{
> -    /**
> -     * @brief Presents the given request to the user, returning the user-provided answer.
> -     * @param request The trust request that a user has to answer.
> -     * @param description Extended description of the trust request.
> -     */
> -    MOCK_METHOD3(prompt_user_for_request, core::trust::Request::Answer(pid_t, const std::string&, const std::string&));
> -};
> -
> -struct MockStore : public core::trust::Store
> -{
> -    struct MockQuery : public core::trust::Store::Query
> -    {
> -        /** @brief Access the status of the query. */
> -        MOCK_CONST_METHOD0(status, core::trust::Store::Query::Status());
> -
> -        /** @brief Limit the query to a specific application Id. */
> -        MOCK_METHOD1(for_application_id, void(const std::string&));
> -
> -        /** @brief Limit the query to a service-specific feature. */
> -        MOCK_METHOD1(for_feature, void(std::uint64_t));
> -
> -        /** @brief Limit the query to the specified time interval. */
> -        MOCK_METHOD2(for_interval, void(const core::trust::Request::Timestamp&, const core::trust::Request::Timestamp&));
> -
> -        /** @brief Limit the query for a specific answer. */
> -        MOCK_METHOD1(for_answer, void(core::trust::Request::Answer));
> -
> -        /** @brief Query all stored requests. */
> -        MOCK_METHOD0(all, void());
> -
> -        /** @brief Execute the query against the store. */
> -        MOCK_METHOD0(execute, void());
> -
> -        /** @brief After successful execution, advance to the next request. */
> -        MOCK_METHOD0(next, void());
> -
> -        /** @brief After successful execution, erase the current element and advance to the next request. */
> -        MOCK_METHOD0(erase, void());
> -
> -        /** @brief Access the request the query currently points to. */
> -        MOCK_METHOD0(current, core::trust::Request());
> -    };
> -
> -    /** @brief Resets the state of the store, implementations should discard
> -     * all persistent and non-persistent state.
> -     */
> -    MOCK_METHOD0(reset, void());
> -
> -    /** @brief Add the provided request to the store. When this function returns true,
> -      * the request has been persisted by the implementation.
> -      */
> -    MOCK_METHOD1(add, void(const core::trust::Request&));
> -
> -    /**
> -     * @brief Create a query for this store.
> -     */
> -    MOCK_METHOD0(query, std::shared_ptr<core::trust::Store::Query>());
> -};
> -
> -pid_t the_default_pid_for_testing()
> -{
> -    return 42;
> -}
> -
> -std::uint64_t the_default_feature_for_testing()
> -{
> -    return 0;
> +core::trust::Pid the_default_pid_for_testing()
> +{
> +    return core::trust::Pid{42};
> +}
> +
> +core::trust::Uid the_default_uid_for_testing()
> +{
> +    return core::trust::Uid{42};
> +}
> +
> +core::trust::Feature the_default_feature_for_testing()
> +{
> +    return core::trust::Feature{0};
>  }
>  
>  std::shared_ptr<core::trust::Agent> a_null_agent()
> @@ -129,6 +76,7 @@
>      {
>          a_null_agent(),
>          a_null_store(),
> +        the_default_uid_for_testing(),
>          the_default_pid_for_testing(),
>          "this.is.just.for.testing.purposes",
>          the_default_feature_for_testing(),
> @@ -217,7 +165,7 @@
>      EXPECT_CALL(*mocked_query, for_feature(params.feature)).Times(1);
>      // The setup ensures that a previously stored answer is available in the store.
>      // For that, the agent should not be queried.
> -    EXPECT_CALL(*mocked_agent, prompt_user_for_request(_, _, _)).Times(0);
> +    EXPECT_CALL(*mocked_agent, authenticate_request_with_parameters(_)).Times(0);
>  
>      params.agent = mocked_agent;
>      params.store = mocked_store;
> @@ -233,6 +181,15 @@
>  
>      auto params = default_request_parameters_for_testing();
>  
> +    core::trust::Agent::RequestParameters agent_params
> +    {
> +        params.application_uid,
> +        params.application_pid,
> +        params.application_id,
> +        params.feature,
> +        params.description
> +    };
> +
>      core::trust::Request request
>      {
>          params.application_id,
> @@ -245,7 +202,7 @@
>      auto mocked_query = a_mocked_query();
>      auto mocked_store = a_mocked_store();
>  
> -    ON_CALL(*mocked_agent, prompt_user_for_request(params.application_pid, params.application_id, params.description))
> +    ON_CALL(*mocked_agent, authenticate_request_with_parameters(agent_params))
>              .WillByDefault(
>                  Return(
>                      answer));
> @@ -270,7 +227,7 @@
>      EXPECT_CALL(*mocked_query, for_feature(params.feature)).Times(1);
>      // The setup ensures that a previously stored answer is available in the store.
>      // For that, the agent should not be queried.
> -    EXPECT_CALL(*mocked_agent, prompt_user_for_request(params.application_pid, params.application_id, params.description)).Times(1);
> +    EXPECT_CALL(*mocked_agent, authenticate_request_with_parameters(agent_params)).Times(1);
>  
>      params.agent = mocked_agent;
>      params.store = mocked_store;
> 
> === modified file 'tests/trust_store_test.cpp'
> --- tests/trust_store_test.cpp	2014-05-06 11:32:13 +0000
> +++ tests/trust_store_test.cpp	2014-07-31 13:42:29 +0000
> @@ -44,7 +44,7 @@
>      core::trust::Request r
>      {
>          "this.does.not.exist.app",
> -        0,
> +        core::trust::Feature{0},
>          std::chrono::system_clock::time_point(std::chrono::seconds{0}),
>          core::trust::Request::Answer::granted
>      };
> @@ -89,16 +89,16 @@
>      core::trust::Request r1
>      {
>          "this.does.not.exist.app",
> -        base_feature,
> -                std::chrono::system_clock::now(),
> -                core::trust::Request::Answer::granted
> +        core::trust::Feature{base_feature},
> +        std::chrono::system_clock::now(),
> +        core::trust::Request::Answer::granted
>      };
>  
>      core::trust::Request r2 = r1;
> -    r2.feature = base_feature + 1;
> +    r2.feature.value = base_feature + 1;
>  
>      core::trust::Request r3 = r2;
> -    r2.feature = base_feature + 2;
> +    r2.feature.value = base_feature + 2;
>  
>      store->add(r1);
>      store->add(r2);
> @@ -128,7 +128,7 @@
>      core::trust::Request r1
>      {
>          app1,
> -        0,
> +        core::trust::Feature{0},
>          std::chrono::system_clock::now(),
>          core::trust::Request::Answer::granted
>      };
> @@ -136,7 +136,7 @@
>      core::trust::Request r2
>      {
>          app2,
> -        0,
> +        core::trust::Feature{0},
>          std::chrono::system_clock::now(),
>          core::trust::Request::Answer::granted
>      };
> @@ -162,7 +162,7 @@
>      core::trust::Request r1
>      {
>          app1,
> -        0,
> +        core::trust::Feature{0},
>          std::chrono::system_clock::now(),
>          core::trust::Request::Answer::granted
>      };
> @@ -170,7 +170,7 @@
>      core::trust::Request r2
>      {
>          app1,
> -        1,
> +        core::trust::Feature{1},
>          std::chrono::system_clock::now(),
>          core::trust::Request::Answer::granted
>      };
> @@ -196,7 +196,7 @@
>      core::trust::Request r1
>      {
>          app1,
> -        0,
> +        core::trust::Feature{0},
>          std::chrono::system_clock::now(),
>          core::trust::Request::Answer::granted
>      };
> @@ -204,7 +204,7 @@
>      core::trust::Request r2
>      {
>          app1,
> -        1,
> +        core::trust::Feature{1},
>          std::chrono::system_clock::now(),
>          core::trust::Request::Answer::denied
>      };
> @@ -230,7 +230,7 @@
>      core::trust::Request r1
>      {
>          app1,
> -        0,
> +        core::trust::Feature{0},
>          std::chrono::system_clock::time_point(std::chrono::seconds{0}),
>          core::trust::Request::Answer::granted
>      };
> @@ -238,7 +238,7 @@
>      core::trust::Request r2
>      {
>          app1,
> -        1,
> +        core::trust::Feature{1},
>          std::chrono::system_clock::time_point(std::chrono::seconds{500}),
>          core::trust::Request::Answer::granted
>      };
> @@ -246,7 +246,7 @@
>      core::trust::Request r3
>      {
>          app1,
> -        1,
> +        core::trust::Feature{1},
>          std::chrono::system_clock::now(),
>          core::trust::Request::Answer::granted
>      };
> @@ -277,7 +277,7 @@
>      core::trust::Request r1
>      {
>          app1,
> -        0,
> +        core::trust::Feature{0},
>          std::chrono::system_clock::time_point(std::chrono::seconds{0}),
>          core::trust::Request::Answer::granted
>      };
> @@ -285,7 +285,7 @@
>      core::trust::Request r2
>      {
>          app1,
> -        1,
> +        core::trust::Feature{1},
>          std::chrono::system_clock::time_point(std::chrono::seconds{500}),
>          core::trust::Request::Answer::granted
>      };
> @@ -293,7 +293,7 @@
>      core::trust::Request r3
>      {
>          app1,
> -        1,
> +        core::trust::Feature{1},
>          std::chrono::system_clock::now(),
>          core::trust::Request::Answer::denied
>      };
> @@ -327,14 +327,14 @@
>          core::trust::Request r
>          {
>              "this.does.not.exist.app",
> -            base_feature,
> -                    std::chrono::system_clock::now(),
> -                    core::trust::Request::Answer::granted
> +            core::trust::Feature{base_feature},
> +            std::chrono::system_clock::now(),
> +            core::trust::Request::Answer::granted
>          };
>  
>          for (unsigned int i = 0; i < 100; i++)
>          {
> -            r.feature = base + i;
> +            r.feature.value = base + i;
>              store->add(r);
>          }
>      };
> @@ -371,14 +371,14 @@
>          core::trust::Request r
>          {
>              "this.does.not.exist.app",
> -            0,
> +            core::trust::Feature{0},
>              std::chrono::system_clock::now(),
>              core::trust::Request::Answer::granted
>          };
>  
>          for (unsigned int i = 0; i < 100; i++)
>          {
> -            r.feature = i;
> +            r.feature.value = i;
>              store->add(r);
>          }
>  
> 


-- 
https://code.launchpad.net/~thomas-voss/trust-store/add-trust-stored/+merge/227685
Your team Ubuntu Phablet Team is subscribed to branch lp:trust-store.



More information about the Ubuntu-reviews mailing list