mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 17:27:35 +00:00
Escalator: Major rework and enable forwarding of stdin/stdout
- Escalator now uses `posix_spawn` instead of `exec` - Escalator is now able to forward stdin and stdout - A few general changes to improve code quality
This commit is contained in:
parent
059904371f
commit
27297d2817
3 changed files with 64 additions and 22 deletions
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Ashley N. <dev-serenity@ne0ndrag0n.com>
|
* Copyright (c) 2022, Ashley N. <dev-serenity@ne0ndrag0n.com>
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
|
* Copyright (c) 2023, Fabian Dellwing <fabian@dellwing.net>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -8,6 +9,7 @@
|
||||||
#include "EscalatorWindow.h"
|
#include "EscalatorWindow.h"
|
||||||
#include <AK/Assertions.h>
|
#include <AK/Assertions.h>
|
||||||
#include <Applications/Escalator/EscalatorGML.h>
|
#include <Applications/Escalator/EscalatorGML.h>
|
||||||
|
#include <LibCore/File.h>
|
||||||
#include <LibCore/SecretString.h>
|
#include <LibCore/SecretString.h>
|
||||||
#include <LibCore/System.h>
|
#include <LibCore/System.h>
|
||||||
#include <LibGUI/FileIconProvider.h>
|
#include <LibGUI/FileIconProvider.h>
|
||||||
|
@ -15,13 +17,16 @@
|
||||||
#include <LibGUI/Label.h>
|
#include <LibGUI/Label.h>
|
||||||
#include <LibGUI/MessageBox.h>
|
#include <LibGUI/MessageBox.h>
|
||||||
#include <LibGUI/Widget.h>
|
#include <LibGUI/Widget.h>
|
||||||
|
#include <fcntl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
EscalatorWindow::EscalatorWindow(StringView executable, Vector<StringView> arguments, EscalatorWindow::Options const& options)
|
EscalatorWindow::EscalatorWindow(StringView executable, Vector<StringView> arguments, EscalatorWindow::Options const& options)
|
||||||
: m_arguments(arguments)
|
: m_arguments(move(arguments))
|
||||||
, m_executable(executable)
|
, m_executable(executable)
|
||||||
, m_current_user(options.current_user)
|
, m_current_user(options.current_user)
|
||||||
, m_preserve_env(options.preserve_env)
|
, m_preserve_env(options.preserve_env)
|
||||||
|
, m_forward_stdin(options.forward_stdin)
|
||||||
|
, m_forward_stdout(options.forward_stdout)
|
||||||
{
|
{
|
||||||
auto app_icon = GUI::FileIconProvider::icon_for_executable(m_executable);
|
auto app_icon = GUI::FileIconProvider::icon_for_executable(m_executable);
|
||||||
|
|
||||||
|
@ -52,8 +57,8 @@ EscalatorWindow::EscalatorWindow(StringView executable, Vector<StringView> argum
|
||||||
auto result = check_password();
|
auto result = check_password();
|
||||||
if (result.is_error()) {
|
if (result.is_error()) {
|
||||||
GUI::MessageBox::show_error(this, DeprecatedString::formatted("Failed to execute command: {}", result.error()));
|
GUI::MessageBox::show_error(this, DeprecatedString::formatted("Failed to execute command: {}", result.error()));
|
||||||
close();
|
|
||||||
}
|
}
|
||||||
|
close();
|
||||||
};
|
};
|
||||||
m_ok_button->set_default(true);
|
m_ok_button->set_default(true);
|
||||||
|
|
||||||
|
@ -83,32 +88,59 @@ ErrorOr<void> EscalatorWindow::check_password()
|
||||||
|
|
||||||
// Caller will close Escalator if error is returned.
|
// Caller will close Escalator if error is returned.
|
||||||
TRY(execute_command());
|
TRY(execute_command());
|
||||||
VERIFY_NOT_REACHED();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorOr<void> EscalatorWindow::execute_command()
|
ErrorOr<void> EscalatorWindow::execute_command()
|
||||||
{
|
{
|
||||||
// Translate environ to format for Core::System::exec.
|
char const* envp[] = { nullptr };
|
||||||
Vector<StringView> exec_environment;
|
Vector<char const*, 4> argv;
|
||||||
for (size_t i = 0; environ[i]; ++i) {
|
for (auto& arg : m_arguments)
|
||||||
StringView env_view { environ[i], strlen(environ[i]) };
|
argv.append(arg.characters_without_null_termination());
|
||||||
auto maybe_needle = env_view.find('=');
|
argv.append(nullptr);
|
||||||
|
|
||||||
if (!maybe_needle.has_value())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!m_preserve_env && env_view.substring_view(0, maybe_needle.value()) != "TERM"sv)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
exec_environment.append(env_view);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Escalate process privilege to root user.
|
// Escalate process privilege to root user.
|
||||||
TRY(Core::System::seteuid(0));
|
TRY(Core::System::seteuid(0));
|
||||||
auto root_user = TRY(Core::Account::from_uid(0));
|
auto root_user = TRY(Core::Account::from_uid(0));
|
||||||
TRY(root_user.login());
|
TRY(root_user.login());
|
||||||
|
|
||||||
TRY(Core::System::pledge("stdio sendfd rpath exec"));
|
if (m_forward_stdin || m_forward_stdout) {
|
||||||
TRY(Core::System::exec(m_executable, m_arguments, Core::System::SearchInPath::No, exec_environment));
|
auto in_pipefds = TRY(Core::System::pipe2(O_CLOEXEC));
|
||||||
VERIFY_NOT_REACHED();
|
auto out_pipefds = TRY(Core::System::pipe2(O_CLOEXEC));
|
||||||
|
ScopeGuard guard_fds { [&] {
|
||||||
|
::close(in_pipefds[1]);
|
||||||
|
::close(out_pipefds[0]);
|
||||||
|
} };
|
||||||
|
{
|
||||||
|
posix_spawn_file_actions_t file_actions;
|
||||||
|
posix_spawn_file_actions_init(&file_actions);
|
||||||
|
posix_spawn_file_actions_adddup2(&file_actions, in_pipefds[0], STDIN_FILENO);
|
||||||
|
posix_spawn_file_actions_adddup2(&file_actions, out_pipefds[1], STDOUT_FILENO);
|
||||||
|
|
||||||
|
ScopeGuard guard_fds_and_file_actions { [&]() {
|
||||||
|
posix_spawn_file_actions_destroy(&file_actions);
|
||||||
|
::close(in_pipefds[0]);
|
||||||
|
::close(out_pipefds[1]);
|
||||||
|
} };
|
||||||
|
|
||||||
|
TRY(Core::System::pledge("stdio sendfd rpath proc exec"));
|
||||||
|
(void)TRY(Core::System::posix_spawn(m_executable, &file_actions, nullptr, const_cast<char* const*>(argv.data()), m_preserve_env ? environ : const_cast<char**>(envp)));
|
||||||
|
|
||||||
|
if (m_forward_stdin) {
|
||||||
|
auto in_outfile = TRY(Core::File::adopt_fd(in_pipefds[1], Core::File::OpenMode::Write, Core::File::ShouldCloseFileDescriptor::No));
|
||||||
|
auto in_infile = TRY(Core::File::standard_input());
|
||||||
|
TRY(in_outfile->write_until_depleted(TRY(in_infile->read_until_eof())));
|
||||||
|
}
|
||||||
|
if (m_forward_stdout) {
|
||||||
|
auto out_outfile = TRY(Core::File::standard_output());
|
||||||
|
auto out_infile = TRY(Core::File::adopt_fd(out_pipefds[0], Core::File::OpenMode::Read, Core::File::ShouldCloseFileDescriptor::No));
|
||||||
|
TRY(out_outfile->write_until_depleted(TRY(out_infile->read_until_eof())));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
TRY(Core::System::pledge("stdio sendfd rpath proc exec"));
|
||||||
|
(void)TRY(Core::System::posix_spawn(m_executable, nullptr, nullptr, const_cast<char* const*>(argv.data()), m_preserve_env ? environ : const_cast<char**>(envp)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Ashley N. <dev-serenity@ne0ndrag0n.com>
|
* Copyright (c) 2022, Ashley N. <dev-serenity@ne0ndrag0n.com>
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
|
* Copyright (c) 2023, Fabian Dellwing <fabian@dellwing.net>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -23,6 +24,8 @@ public:
|
||||||
StringView description;
|
StringView description;
|
||||||
Core::Account current_user;
|
Core::Account current_user;
|
||||||
bool preserve_env { false };
|
bool preserve_env { false };
|
||||||
|
bool forward_stdin { false };
|
||||||
|
bool forward_stdout { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ~EscalatorWindow() override = default;
|
virtual ~EscalatorWindow() override = default;
|
||||||
|
@ -38,6 +41,8 @@ private:
|
||||||
StringView m_executable;
|
StringView m_executable;
|
||||||
Core::Account m_current_user;
|
Core::Account m_current_user;
|
||||||
bool m_preserve_env { false };
|
bool m_preserve_env { false };
|
||||||
|
bool m_forward_stdin { false };
|
||||||
|
bool m_forward_stdout { false };
|
||||||
|
|
||||||
RefPtr<GUI::ImageWidget> m_icon_image_widget;
|
RefPtr<GUI::ImageWidget> m_icon_image_widget;
|
||||||
RefPtr<GUI::Button> m_ok_button;
|
RefPtr<GUI::Button> m_ok_button;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2022, Ashley N. <dev-serenity@ne0ndrag0n.com>
|
* Copyright (c) 2022, Ashley N. <dev-serenity@ne0ndrag0n.com>
|
||||||
* Copyright (c) 2022, the SerenityOS developers.
|
* Copyright (c) 2022, the SerenityOS developers.
|
||||||
|
* Copyright (c) 2023, Fabian Dellwing <fabian@dellwing.net>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -22,10 +23,14 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
Core::ArgsParser args_parser;
|
Core::ArgsParser args_parser;
|
||||||
StringView description;
|
StringView description;
|
||||||
bool preserve_env = false;
|
bool preserve_env = false;
|
||||||
|
bool forward_stdin = false;
|
||||||
|
bool forward_stdout = false;
|
||||||
args_parser.set_general_help("Escalate privilege to root for a given command using a GUI prompt.");
|
args_parser.set_general_help("Escalate privilege to root for a given command using a GUI prompt.");
|
||||||
args_parser.set_stop_on_first_non_option(true);
|
args_parser.set_stop_on_first_non_option(true);
|
||||||
args_parser.add_option(description, "Custom prompt to use for dialog", "prompt", 'P', "prompt");
|
args_parser.add_option(description, "Custom prompt to use for dialog", "prompt", 'P', "prompt");
|
||||||
args_parser.add_option(preserve_env, "Preserve user environment when running command", "preserve-env", 'E');
|
args_parser.add_option(preserve_env, "Preserve user environment when running command", "preserve-env", 'E');
|
||||||
|
args_parser.add_option(forward_stdin, "Forward stdin to targets stdin", "forward-stdin", 'I');
|
||||||
|
args_parser.add_option(forward_stdout, "Forward targets stdout to stdout", "forward-stdout", 'O');
|
||||||
args_parser.add_positional_argument(command, "Command to run at elevated privilege level", "command");
|
args_parser.add_positional_argument(command, "Command to run at elevated privilege level", "command");
|
||||||
args_parser.parse(arguments);
|
args_parser.parse(arguments);
|
||||||
|
|
||||||
|
@ -33,14 +38,14 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
|
||||||
|
|
||||||
auto app = TRY(GUI::Application::create(arguments));
|
auto app = TRY(GUI::Application::create(arguments));
|
||||||
|
|
||||||
auto executable_path = FileSystem::resolve_executable_from_environment(command[0]);
|
auto executable_path = FileSystem::resolve_executable_from_environment(command[0], AT_EACCESS);
|
||||||
if (executable_path.is_error()) {
|
if (executable_path.is_error()) {
|
||||||
GUI::MessageBox::show_error(nullptr, DeprecatedString::formatted("Could not execute command {}: Command not found.", command[0]));
|
GUI::MessageBox::show_error(nullptr, DeprecatedString::formatted("Could not execute command {}: Command not found.", command[0]));
|
||||||
return 127;
|
return 127;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto current_user = TRY(Core::Account::self());
|
auto current_user = TRY(Core::Account::self());
|
||||||
auto window = TRY(EscalatorWindow::try_create(executable_path.value(), command, EscalatorWindow::Options { description, current_user, preserve_env }));
|
auto window = TRY(EscalatorWindow::try_create(executable_path.value(), command, EscalatorWindow::Options { description, current_user, preserve_env, forward_stdin, forward_stdout }));
|
||||||
|
|
||||||
if (current_user.uid() != 0) {
|
if (current_user.uid() != 0) {
|
||||||
window->show();
|
window->show();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue