summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Kernel/API/Syscall.h1
-rw-r--r--Kernel/API/Unveil.h22
-rw-r--r--Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.cpp5
-rw-r--r--Kernel/Process.cpp6
-rw-r--r--Kernel/Process.h7
-rw-r--r--Kernel/Syscalls/execve.cpp21
-rw-r--r--Kernel/Syscalls/fork.cpp8
-rw-r--r--Kernel/Syscalls/unveil.cpp120
-rw-r--r--Userland/Libraries/LibC/unistd.cpp2
-rw-r--r--Userland/Libraries/LibCore/System.cpp16
-rw-r--r--Userland/Libraries/LibCore/System.h1
11 files changed, 161 insertions, 48 deletions
diff --git a/Kernel/API/Syscall.h b/Kernel/API/Syscall.h
index 4e40fdce16..f306e549b1 100644
--- a/Kernel/API/Syscall.h
+++ b/Kernel/API/Syscall.h
@@ -433,6 +433,7 @@ struct SC_pledge_params {
};
struct SC_unveil_params {
+ int flags;
StringArgument path;
StringArgument permissions;
};
diff --git a/Kernel/API/Unveil.h b/Kernel/API/Unveil.h
new file mode 100644
index 0000000000..7d116f8e41
--- /dev/null
+++ b/Kernel/API/Unveil.h
@@ -0,0 +1,22 @@
+/*
+ * Copyright (c) 2022, Liav A. <liavalb@hotmail.co.il>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#pragma once
+
+#include <AK/EnumBits.h>
+#include <AK/Types.h>
+
+namespace Kernel {
+
+enum class UnveilFlags : u32 {
+ None = 0,
+ CurrentProgram = 1 << 0,
+ AfterExec = 1 << 1,
+};
+
+AK_ENUM_BITWISE_OPERATORS(UnveilFlags);
+
+}
diff --git a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.cpp b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.cpp
index 50ddbf3b18..10500a3608 100644
--- a/Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.cpp
+++ b/Kernel/FileSystem/SysFS/Subsystems/Kernel/Processes.cpp
@@ -53,6 +53,11 @@ ErrorOr<void> SysFSOverallProcesses::try_generate(KBufferBuilder& builder)
case VeilState::Locked:
TRY(process_object.add("veil"sv, "Locked"));
break;
+ case VeilState::LockedInherited:
+ // Note: We don't reveal if the locked state is either by our choice
+ // or someone else applied it.
+ TRY(process_object.add("veil"sv, "Locked"));
+ break;
}
} else {
TRY(process_object.add("pledge"sv, ""sv));
diff --git a/Kernel/Process.cpp b/Kernel/Process.cpp
index 5346a4880e..8fe1b42961 100644
--- a/Kernel/Process.cpp
+++ b/Kernel/Process.cpp
@@ -320,13 +320,14 @@ ErrorOr<NonnullLockRefPtr<Process>> Process::try_create(LockRefPtr<Thread>& firs
new_address_space = TRY(Memory::AddressSpace::try_create(nullptr));
}
auto unveil_tree = UnveilNode { TRY(KString::try_create("/"sv)), UnveilMetadata(TRY(KString::try_create("/"sv))) };
+ auto exec_unveil_tree = UnveilNode { TRY(KString::try_create("/"sv)), UnveilMetadata(TRY(KString::try_create("/"sv))) };
auto credentials = TRY(Credentials::create(uid, gid, uid, gid, uid, gid, {}));
- auto process = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Process(move(name), move(credentials), ppid, is_kernel_process, move(current_directory), move(executable), tty, move(unveil_tree))));
+ auto process = TRY(adopt_nonnull_lock_ref_or_enomem(new (nothrow) Process(move(name), move(credentials), ppid, is_kernel_process, move(current_directory), move(executable), tty, move(unveil_tree), move(exec_unveil_tree))));
TRY(process->attach_resources(new_address_space.release_nonnull(), first_thread, fork_parent));
return process;
}
-Process::Process(NonnullOwnPtr<KString> name, NonnullRefPtr<Credentials> credentials, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, TTY* tty, UnveilNode unveil_tree)
+Process::Process(NonnullOwnPtr<KString> name, NonnullRefPtr<Credentials> credentials, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, TTY* tty, UnveilNode unveil_tree, UnveilNode exec_unveil_tree)
: m_name(move(name))
, m_space(LockRank::None)
, m_protected_data_lock(LockRank::None)
@@ -335,6 +336,7 @@ Process::Process(NonnullOwnPtr<KString> name, NonnullRefPtr<Credentials> credent
, m_current_directory(LockRank::None, move(current_directory))
, m_tty(tty)
, m_unveil_data(LockRank::None, move(unveil_tree))
+ , m_exec_unveil_data(LockRank::None, move(exec_unveil_tree))
, m_wait_blocker_set(*this)
{
// Ensure that we protect the process data when exiting the constructor.
diff --git a/Kernel/Process.h b/Kernel/Process.h
index a5bfa6ae90..bf0c1616c2 100644
--- a/Kernel/Process.h
+++ b/Kernel/Process.h
@@ -84,6 +84,7 @@ enum class VeilState {
None,
Dropped,
Locked,
+ LockedInherited,
};
static constexpr FlatPtr futex_key_private_flag = 0b1;
@@ -523,6 +524,9 @@ public:
auto& unveil_data() { return m_unveil_data; }
auto const& unveil_data() const { return m_unveil_data; }
+ auto& exec_unveil_data() { return m_exec_unveil_data; }
+ auto const& exec_unveil_data() const { return m_exec_unveil_data; }
+
bool wait_for_tracer_at_next_execve() const
{
return m_wait_for_tracer_at_next_execve;
@@ -584,7 +588,7 @@ private:
bool add_thread(Thread&);
bool remove_thread(Thread&);
- Process(NonnullOwnPtr<KString> name, NonnullRefPtr<Credentials>, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, TTY* tty, UnveilNode unveil_tree);
+ Process(NonnullOwnPtr<KString> name, NonnullRefPtr<Credentials>, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory, RefPtr<Custody> executable, TTY* tty, UnveilNode unveil_tree, UnveilNode exec_unveil_tree);
static ErrorOr<NonnullLockRefPtr<Process>> try_create(LockRefPtr<Thread>& first_thread, NonnullOwnPtr<KString> name, UserID, GroupID, ProcessID ppid, bool is_kernel_process, RefPtr<Custody> current_directory = nullptr, RefPtr<Custody> executable = nullptr, TTY* = nullptr, Process* fork_parent = nullptr);
ErrorOr<void> attach_resources(NonnullOwnPtr<Memory::AddressSpace>&&, LockRefPtr<Thread>& first_thread, Process* fork_parent);
static ProcessID allocate_pid();
@@ -878,6 +882,7 @@ private:
LockRefPtr<Timer> m_alarm_timer;
SpinlockProtected<UnveilData> m_unveil_data;
+ SpinlockProtected<UnveilData> m_exec_unveil_data;
OwnPtr<PerformanceEventBuffer> m_perf_event_buffer;
diff --git a/Kernel/Syscalls/execve.cpp b/Kernel/Syscalls/execve.cpp
index 713e1b8832..7026f6090b 100644
--- a/Kernel/Syscalls/execve.cpp
+++ b/Kernel/Syscalls/execve.cpp
@@ -560,9 +560,24 @@ ErrorOr<void> Process::do_exec(NonnullLockRefPtr<OpenFileDescription> main_progr
m_environment = move(environment);
TRY(m_unveil_data.with([&](auto& unveil_data) -> ErrorOr<void> {
- unveil_data.state = VeilState::None;
- unveil_data.paths.clear();
- unveil_data.paths.set_metadata({ TRY(KString::try_create("/"sv)), UnveilAccess::None, false });
+ TRY(m_exec_unveil_data.with([&](auto& exec_unveil_data) -> ErrorOr<void> {
+ // Note: If we have exec unveil data being waiting to be dispatched
+ // to the current execve'd program, then we apply the unveil data and
+ // ensure it is locked in the new program.
+ if (exec_unveil_data.state == VeilState::Dropped) {
+ unveil_data.state = VeilState::LockedInherited;
+ exec_unveil_data.state = VeilState::None;
+ unveil_data.paths = TRY(exec_unveil_data.paths.deep_copy());
+ } else {
+ unveil_data.state = VeilState::None;
+ exec_unveil_data.state = VeilState::None;
+ unveil_data.paths.clear();
+ unveil_data.paths.set_metadata({ TRY(KString::try_create("/"sv)), UnveilAccess::None, false });
+ }
+ exec_unveil_data.paths.clear();
+ exec_unveil_data.paths.set_metadata({ TRY(KString::try_create("/"sv)), UnveilAccess::None, false });
+ return {};
+ }));
return {};
}));
diff --git a/Kernel/Syscalls/fork.cpp b/Kernel/Syscalls/fork.cpp
index 0fdcd53bd4..111636a00e 100644
--- a/Kernel/Syscalls/fork.cpp
+++ b/Kernel/Syscalls/fork.cpp
@@ -42,6 +42,14 @@ ErrorOr<FlatPtr> Process::sys$fork(RegisterState& regs)
});
}));
+ TRY(m_exec_unveil_data.with([&](auto& parent_exec_unveil_data) -> ErrorOr<void> {
+ return child->m_exec_unveil_data.with([&](auto& child_exec_unveil_data) -> ErrorOr<void> {
+ child_exec_unveil_data.state = parent_exec_unveil_data.state;
+ child_exec_unveil_data.paths = TRY(parent_exec_unveil_data.paths.deep_copy());
+ return {};
+ });
+ }));
+
// Note: We take the spinlock of Process::all_instances list because we need
// to ensure that when we take the jail spinlock of two processes that we don't
// run into a deadlock situation because both processes compete over each other Jail's
diff --git a/Kernel/Syscalls/unveil.cpp b/Kernel/Syscalls/unveil.cpp
index 496791b03f..83e53cc5a0 100644
--- a/Kernel/Syscalls/unveil.cpp
+++ b/Kernel/Syscalls/unveil.cpp
@@ -7,6 +7,7 @@
#include <AK/RefPtr.h>
#include <AK/StringView.h>
+#include <Kernel/API/Unveil.h>
#include <Kernel/FileSystem/Custody.h>
#include <Kernel/FileSystem/VirtualFileSystem.h>
#include <Kernel/KLexicalPath.h>
@@ -25,6 +26,55 @@ static void update_intermediate_node_permissions(UnveilNode& root_node, UnveilAc
}
}
+static ErrorOr<void> update_unveil_data(Process::UnveilData& locked_unveil_data, StringView unveiled_path, UnveilAccess new_permissions)
+{
+ auto path_parts = KLexicalPath::parts(unveiled_path);
+ auto it = path_parts.begin();
+ // Note: For the sake of completence, we check if the locked state was inherited
+ // by an execve'd sequence. If that is the case, just silently ignore this.
+ if (locked_unveil_data.state == VeilState::LockedInherited)
+ return {};
+ // NOTE: We have to check again, since the veil may have been locked by another thread
+ // while we were parsing the arguments.
+ if (locked_unveil_data.state == VeilState::Locked)
+ return EPERM;
+
+ auto& matching_node = locked_unveil_data.paths.traverse_until_last_accessible_node(it, path_parts.end());
+ if (it.is_end()) {
+ // If the path has already been explicitly unveiled, do not allow elevating its permissions.
+ if (matching_node.was_explicitly_unveiled()) {
+ if (new_permissions & ~matching_node.permissions())
+ return EPERM;
+ }
+
+ // It is possible that nodes that are "grandchildren" of the matching node have already been unveiled.
+ // This means that there may be intermediate nodes between this one and the unveiled "grandchildren"
+ // that inherited the current node's previous permissions. Those nodes now need their permissions
+ // updated to match the current node.
+ if (matching_node.permissions() != new_permissions)
+ update_intermediate_node_permissions(matching_node, new_permissions);
+
+ matching_node.metadata_value().explicitly_unveiled = true;
+ matching_node.metadata_value().permissions = new_permissions;
+ locked_unveil_data.state = VeilState::Dropped;
+ return {};
+ }
+
+ auto new_unveiled_path = TRY(KString::try_create(unveiled_path));
+ TRY(matching_node.insert(
+ it,
+ path_parts.end(),
+ { move(new_unveiled_path), new_permissions, true },
+ [](auto& parent, auto& it) -> ErrorOr<Optional<UnveilMetadata>> {
+ auto path = TRY(KString::formatted("{}/{}", parent.path(), *it));
+ return UnveilMetadata(move(path), parent.permissions(), false);
+ }));
+
+ VERIFY(locked_unveil_data.state != VeilState::Locked);
+ locked_unveil_data.state = VeilState::Dropped;
+ return {};
+}
+
ErrorOr<FlatPtr> Process::sys$unveil(Userspace<Syscall::SC_unveil_params const*> user_params)
{
VERIFY_NO_PROCESS_BIG_LOCK(this);
@@ -35,7 +85,17 @@ ErrorOr<FlatPtr> Process::sys$unveil(Userspace<Syscall::SC_unveil_params const*>
return 0;
}
- if (veil_state() == VeilState::Locked)
+ if (!((params.flags & to_underlying(UnveilFlags::CurrentProgram)) || (params.flags & to_underlying(UnveilFlags::AfterExec))))
+ return EINVAL;
+
+ // Note: If we inherited a locked state, then silently ignore the unveil request,
+ // and let the user program potentially deal with an ENOENT error later on.
+ if ((params.flags & static_cast<unsigned>(UnveilFlags::CurrentProgram)) && veil_state() == VeilState::LockedInherited)
+ return 0;
+
+ // Note: We only lock the unveil state for current program, while allowing adding
+ // indefinitely unveil data before doing the actual exec().
+ if ((params.flags & static_cast<unsigned>(UnveilFlags::CurrentProgram)) && veil_state() == VeilState::Locked)
return EPERM;
if (!params.path.characters || !params.permissions.characters)
@@ -93,48 +153,24 @@ ErrorOr<FlatPtr> Process::sys$unveil(Userspace<Syscall::SC_unveil_params const*>
return custody_or_error.release_error();
}
- auto path_parts = KLexicalPath::parts(new_unveiled_path->view());
- auto it = path_parts.begin();
- return m_unveil_data.with([&](auto& unveil_data) -> ErrorOr<FlatPtr> {
- // NOTE: We have to check again, since the veil may have been locked by another thread
- // while we were parsing the arguments.
- if (unveil_data.state == VeilState::Locked)
- return EPERM;
-
- auto& matching_node = unveil_data.paths.traverse_until_last_accessible_node(it, path_parts.end());
- if (it.is_end()) {
- // If the path has already been explicitly unveiled, do not allow elevating its permissions.
- if (matching_node.was_explicitly_unveiled()) {
- if (new_permissions & ~matching_node.permissions())
- return EPERM;
- }
-
- // It is possible that nodes that are "grandchildren" of the matching node have already been unveiled.
- // This means that there may be intermediate nodes between this one and the unveiled "grandchildren"
- // that inherited the current node's previous permissions. Those nodes now need their permissions
- // updated to match the current node.
- if (matching_node.permissions() != new_permissions)
- update_intermediate_node_permissions(matching_node, (UnveilAccess)new_permissions);
-
- matching_node.metadata_value().explicitly_unveiled = true;
- matching_node.metadata_value().permissions = (UnveilAccess)new_permissions;
- unveil_data.state = VeilState::Dropped;
- return 0;
- }
+ if (params.flags & static_cast<unsigned>(UnveilFlags::CurrentProgram)) {
+ TRY(unveil_data().with([&](auto& data) -> ErrorOr<void> {
+ TRY(update_unveil_data(data, new_unveiled_path->view(), static_cast<UnveilAccess>(new_permissions)));
+ return {};
+ }));
+ }
- TRY(matching_node.insert(
- it,
- path_parts.end(),
- { new_unveiled_path.release_nonnull(), (UnveilAccess)new_permissions, true },
- [](auto& parent, auto& it) -> ErrorOr<Optional<UnveilMetadata>> {
- auto path = TRY(KString::formatted("{}/{}", parent.path(), *it));
- return UnveilMetadata(move(path), parent.permissions(), false);
- }));
-
- VERIFY(unveil_data.state != VeilState::Locked);
- unveil_data.state = VeilState::Dropped;
- return 0;
- });
+ if (params.flags & static_cast<unsigned>(UnveilFlags::AfterExec)) {
+ TRY(exec_unveil_data().with([&](auto& data) -> ErrorOr<void> {
+ // Note: The only valid way to get into this state is by using unveil before doing
+ // an actual exec with the UnveilFlags::AfterExec flag. Then this state is applied on
+ // the actual new program unveil data, and never on the m_exec_unveil_data.
+ VERIFY(data.state != VeilState::LockedInherited);
+ TRY(update_unveil_data(data, new_unveiled_path->view(), static_cast<UnveilAccess>(new_permissions)));
+ return {};
+ }));
+ }
+ return 0;
}
}
diff --git a/Userland/Libraries/LibC/unistd.cpp b/Userland/Libraries/LibC/unistd.cpp
index d866e15165..b48dadec11 100644
--- a/Userland/Libraries/LibC/unistd.cpp
+++ b/Userland/Libraries/LibC/unistd.cpp
@@ -8,6 +8,7 @@
#include <AK/ScopedValueRollback.h>
#include <AK/String.h>
#include <AK/Vector.h>
+#include <Kernel/API/Unveil.h>
#include <LibCore/File.h>
#include <alloca.h>
#include <assert.h>
@@ -965,6 +966,7 @@ int pledge(char const* promises, char const* execpromises)
int unveil(char const* path, char const* permissions)
{
Syscall::SC_unveil_params params {
+ static_cast<int>(UnveilFlags::CurrentProgram),
{ path, path ? strlen(path) : 0 },
{ permissions, permissions ? strlen(permissions) : 0 }
};
diff --git a/Userland/Libraries/LibCore/System.cpp b/Userland/Libraries/LibCore/System.cpp
index 39602c5846..3c1ab3264b 100644
--- a/Userland/Libraries/LibCore/System.cpp
+++ b/Userland/Libraries/LibCore/System.cpp
@@ -25,6 +25,7 @@
#include <unistd.h>
#ifdef AK_OS_SERENITY
+# include <Kernel/API/Unveil.h>
# include <LibCore/Account.h>
# include <LibSystem/syscall.h>
# include <serenity.h>
@@ -91,6 +92,7 @@ static ErrorOr<void> unveil_dynamic_loader()
constexpr auto dynamic_loader_permissions = "x"sv;
Syscall::SC_unveil_params params {
+ static_cast<int>(UnveilFlags::CurrentProgram),
{ dynamic_loader_path.characters_without_null_termination(), dynamic_loader_path.length() },
{ dynamic_loader_permissions.characters_without_null_termination(), dynamic_loader_permissions.length() },
};
@@ -110,6 +112,20 @@ ErrorOr<void> unveil(StringView path, StringView permissions)
TRY(unveil_dynamic_loader());
Syscall::SC_unveil_params params {
+ static_cast<int>(UnveilFlags::CurrentProgram),
+ { parsed_path.characters(), parsed_path.length() },
+ { permissions.characters_without_null_termination(), permissions.length() },
+ };
+ int rc = syscall(SC_unveil, &params);
+ HANDLE_SYSCALL_RETURN_VALUE("unveil", rc, {});
+}
+
+ErrorOr<void> unveil_after_exec(StringView path, StringView permissions)
+{
+ auto const parsed_path = TRY(Core::SessionManagement::parse_path_with_sid(path));
+
+ Syscall::SC_unveil_params params {
+ static_cast<int>(UnveilFlags::AfterExec),
{ parsed_path.characters(), parsed_path.length() },
{ permissions.characters_without_null_termination(), permissions.length() },
};
diff --git a/Userland/Libraries/LibCore/System.h b/Userland/Libraries/LibCore/System.h
index 0a47c4ead7..e0f49b0982 100644
--- a/Userland/Libraries/LibCore/System.h
+++ b/Userland/Libraries/LibCore/System.h
@@ -36,6 +36,7 @@ namespace Core::System {
ErrorOr<void> beep();
ErrorOr<void> pledge(StringView promises, StringView execpromises = {});
ErrorOr<void> unveil(StringView path, StringView permissions);
+ErrorOr<void> unveil_after_exec(StringView path, StringView permissions);
ErrorOr<void> sendfd(int sockfd, int fd);
ErrorOr<int> recvfd(int sockfd, int options);
ErrorOr<void> ptrace_peekbuf(pid_t tid, void const* tracee_addr, Bytes destination_buf);