[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