2
* Copyright © 2014 Canonical Ltd.
4
* This program is free software: you can redistribute it and/or modify it
5
* under the terms of the GNU Lesser General Public License version 3,
6
* as published by the Free Software Foundation.
8
* This program is distributed in the hope that it will be useful,
9
* but WITHOUT ANY WARRANTY; without even the implied warranty of
10
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
* GNU Lesser General Public License for more details.
13
* You should have received a copy of the GNU Lesser General Public License
14
* along with this program. If not, see <http://www.gnu.org/licenses/>.
16
* Authored by: Thomas Voß <thomas.voss@canonical.com>
21
#include "prompt_main.h"
23
namespace mir = core::trust::mir;
25
// Invoked whenever a request for creation of pre-authenticated fds succeeds.
26
void mir::PromptSessionVirtualTable::mir_client_fd_callback(MirPromptSession */*prompt_session*/, size_t count, int const* fds, void* context)
31
auto ctxt = static_cast<mir::PromptSessionVirtualTable::Context*>(context);
39
mir::PromptSessionVirtualTable::PromptSessionVirtualTable(MirPromptSession* prompt_session)
40
: prompt_session(prompt_session)
44
int mir::PromptSessionVirtualTable::new_fd_for_prompt_provider()
46
static const unsigned int fd_count = 1;
48
mir::PromptSessionVirtualTable::Context context;
50
mir_wait_for(mir_prompt_session_new_fds_for_prompt_providers(
53
PromptSessionVirtualTable::mir_client_fd_callback,
56
if (context.fd == Context::invalid_fd) throw std::runtime_error
58
"Could not acquire pre-authenticated file descriptors for Mir prompt session."
64
void mir::PromptSessionVirtualTable::release_sync()
66
mir_prompt_session_release_sync(prompt_session);
69
mir::ConnectionVirtualTable::ConnectionVirtualTable(MirConnection* connection)
70
: connection{connection}
74
mir::PromptSessionVirtualTable::Ptr mir::ConnectionVirtualTable::create_prompt_session_sync(
75
// The process id of the requesting app/service
77
// Callback handling prompt session state changes.
78
mir_prompt_session_state_change_callback cb,
82
return PromptSessionVirtualTable::Ptr
84
new PromptSessionVirtualTable
86
mir_connection_create_prompt_session_sync(connection, app_pid, cb, context)
91
mir::PromptProviderHelper::PromptProviderHelper(
92
const mir::PromptProviderHelper::CreationArguments& args) : creation_arguments(args)
96
core::posix::ChildProcess mir::PromptProviderHelper::exec_prompt_provider_with_arguments(
97
const mir::PromptProviderHelper::InvocationArguments& args)
99
static auto child_setup = []() {};
101
std::vector<std::string> argv
103
"--" + std::string{core::trust::mir::cli::option_server_socket}, "fd://" + std::to_string(args.fd),
104
"--" + std::string{core::trust::mir::cli::option_title}, args.application_id,
105
"--" + std::string{core::trust::mir::cli::option_description}, args.description
108
// We just copy the environment
109
std::map<std::string, std::string> env;
110
core::posix::this_process::env::for_each([&env](const std::string& key, const std::string& value)
112
env.insert(std::make_pair(key, value));
116
auto result = core::posix::exec(creation_arguments.path_to_helper_executable,
119
core::posix::StandardStream::empty,
125
void mir::Agent::on_trust_session_changed_state(
126
// The prompt session instance that just changed state.
127
MirPromptSession* /*prompt_provider*/,
128
// The new state of the prompt session instance.
129
MirPromptSessionState state,
130
// The context of type context.
133
if (mir_prompt_session_state_stopped != state)
136
auto ctxt = static_cast<mir::Agent::OnTrustSessionStateChangedCallbackContext*>(context);
142
// If the trust session ended (for whatever reason), we send a SIG_KILL to the
143
// prompt provider process. We hereby ensure that we never return Answer::granted
144
// unless the prompt provider cleanly exited prior to the trust session stopping.
145
ctxt->prompt_provider_process.send_signal(core::posix::Signal::sig_kill, ec);
146
// The required wait for the child process happens in prompt_user_for_request(...).
147
// TODO(tvoss): We should log ec in case of errors.
150
std::function<core::trust::Request::Answer(const core::posix::wait::Result&)> mir::Agent::translator_only_accepting_exit_status_success()
152
return [](const core::posix::wait::Result& result) -> core::trust::Request::Answer
154
// We now analyze the result of the process execution.
155
if (core::posix::wait::Result::Status::exited != result.status) throw std::logic_error
157
"The prompt provider process was signaled or stopped, "
158
"unable to determine a conclusive answer from the user"
161
// If the child process did not exit cleanly, we deny access to the resource.
162
if (core::posix::exit::Status::failure == result.detail.if_exited.status)
163
return core::trust::Request::Answer::denied;
165
return core::trust::Request::Answer::granted;
170
// VTable object providing access to Mir's trusted prompting functionality.
171
const mir::ConnectionVirtualTable::Ptr& connection_vtable,
172
// Exec helper for starting up prompt provider child processes with the correct setup
173
// of command line arguments and environment variables.
174
const mir::PromptProviderHelper::Ptr& exec_helper,
175
// A translator function for mapping child process exit states to trust::Request answers.
176
const std::function<core::trust::Request::Answer(const core::posix::wait::Result&)>& translator)
177
: connection_vtable(connection_vtable),
178
exec_helper(exec_helper),
179
translator(translator)
183
// From core::trust::Agent:
184
core::trust::Request::Answer mir::Agent::prompt_user_for_request(pid_t app_pid, const std::string& app_id, const std::string& description)
186
// We initialize our callback context with an invalid child-process for setup
187
// purposes. Later on, once we have acquired a pre-authenticated fd for the
188
// prompt provider, we exec the actual provider in a child process and replace the
190
mir::Agent::OnTrustSessionStateChangedCallbackContext cb_context
192
core::posix::ChildProcess::invalid()
195
// We ensure that the prompt session is always released cleanly, either on return or on throw.
198
~Scope() { prompt_session->release_sync(); }
199
mir::PromptSessionVirtualTable::Ptr prompt_session;
202
// We setup the prompt session and wire up to our own internal callback helper.
203
connection_vtable->create_prompt_session_sync(
205
Agent::on_trust_session_changed_state,
209
// Acquire a new fd for the prompt provider.
210
auto fd = scope.prompt_session->new_fd_for_prompt_provider();
212
// And prepare the actual execution in a child process.
213
mir::PromptProviderHelper::InvocationArguments args
220
// Ask the helper to fire up the prompt provider.
221
cb_context.prompt_provider_process = exec_helper->exec_prompt_provider_with_arguments(args);
222
// And subsequently wait for it to finish.
223
auto result = cb_context.prompt_provider_process.wait_for(core::posix::wait::Flags::untraced);
225
return translator(result);
228
bool mir::operator==(const mir::PromptProviderHelper::InvocationArguments& lhs, const mir::PromptProviderHelper::InvocationArguments& rhs)
230
return std::tie(lhs.application_id, lhs.description, lhs.fd) == std::tie(rhs.application_id, rhs.description, rhs.fd);
233
#include <core/trust/mir_agent.h>
237
std::shared_ptr<core::trust::Agent> mir::create_agent_for_mir_connection(MirConnection* connection)
239
mir::ConnectionVirtualTable::Ptr cvt
241
new mir::ConnectionVirtualTable
247
mir::PromptProviderHelper::Ptr pph
249
new mir::PromptProviderHelper
251
mir::PromptProviderHelper::CreationArguments
253
core::trust::mir::trust_prompt_executable_in_lib_dir
258
return mir::Agent::Ptr
264
mir::Agent::translator_only_accepting_exit_status_success()