mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2024-11-25 00:50:22 +00:00
LibCore: Remove unused classes and headers
This commit is contained in:
parent
8edaec79de
commit
d8c36ed458
Notes:
github-actions[bot]
2024-10-05 07:21:53 +00:00
Author: https://github.com/rmg-x Commit: https://github.com/LadybirdBrowser/ladybird/commit/d8c36ed4585 Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/1634
20 changed files with 0 additions and 1846 deletions
|
@ -44,7 +44,6 @@ source_set("sources") {
|
|||
"ConfigFile.h",
|
||||
"DateTime.cpp",
|
||||
"DateTime.h",
|
||||
"Debounce.h",
|
||||
"DeferredInvocationContext.h",
|
||||
"ElapsedTimer.cpp",
|
||||
"ElapsedTimer.h",
|
||||
|
@ -58,8 +57,6 @@ source_set("sources") {
|
|||
"EventLoopImplementationUnix.h",
|
||||
"EventReceiver.cpp",
|
||||
"EventReceiver.h",
|
||||
"LockFile.cpp",
|
||||
"LockFile.h",
|
||||
"MappedFile.cpp",
|
||||
"MappedFile.h",
|
||||
"MimeData.cpp",
|
||||
|
@ -71,18 +68,12 @@ source_set("sources") {
|
|||
"Platform/ProcessStatistics.h",
|
||||
"Process.cpp",
|
||||
"Process.h",
|
||||
"ProcessStatisticsReader.cpp",
|
||||
"ProcessStatisticsReader.h",
|
||||
"Promise.h",
|
||||
"Proxy.h",
|
||||
"Resource.cpp",
|
||||
"Resource.h",
|
||||
"ResourceImplementation.cpp",
|
||||
"ResourceImplementationFile.cpp",
|
||||
"SOCKSProxyClient.cpp",
|
||||
"SOCKSProxyClient.h",
|
||||
"SecretString.cpp",
|
||||
"SecretString.h",
|
||||
"SessionManagement.cpp",
|
||||
"SessionManagement.h",
|
||||
"SharedCircularQueue.h",
|
||||
|
@ -100,18 +91,9 @@ source_set("sources") {
|
|||
"Timer.h",
|
||||
"UDPServer.cpp",
|
||||
"UDPServer.h",
|
||||
"UmaskScope.h",
|
||||
]
|
||||
if (current_os != "android") {
|
||||
sources += [
|
||||
"Account.cpp",
|
||||
"Account.h",
|
||||
"FilePermissionsMask.cpp",
|
||||
"FilePermissionsMask.h",
|
||||
"GetPassword.cpp",
|
||||
"GetPassword.h",
|
||||
"Group.cpp",
|
||||
"Group.h",
|
||||
"LocalServer.cpp",
|
||||
"LocalServer.h",
|
||||
]
|
||||
|
|
|
@ -1,383 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Peter Elliott <pelliott@serenityos.org>
|
||||
* Copyright (c) 2021-2022, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Base64.h>
|
||||
#include <AK/Memory.h>
|
||||
#include <AK/Random.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <LibCore/Account.h>
|
||||
#include <LibCore/Directory.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibCore/UmaskScope.h>
|
||||
#include <errno.h>
|
||||
#include <grp.h>
|
||||
#include <pwd.h>
|
||||
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_HAIKU)
|
||||
# include <crypt.h>
|
||||
# include <shadow.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
static ByteString get_salt()
|
||||
{
|
||||
char random_data[12];
|
||||
fill_with_random({ random_data, sizeof(random_data) });
|
||||
|
||||
StringBuilder builder;
|
||||
builder.append("$5$"sv);
|
||||
|
||||
// FIXME: change to TRY() and make method fallible
|
||||
auto salt_string = MUST(encode_base64({ random_data, sizeof(random_data) }));
|
||||
builder.append(salt_string);
|
||||
|
||||
return builder.to_byte_string();
|
||||
}
|
||||
|
||||
static Vector<gid_t> get_extra_gids(passwd const& pwd)
|
||||
{
|
||||
StringView username { pwd.pw_name, strlen(pwd.pw_name) };
|
||||
Vector<gid_t> extra_gids;
|
||||
setgrent();
|
||||
for (auto* group = getgrent(); group; group = getgrent()) {
|
||||
if (group->gr_gid == pwd.pw_gid)
|
||||
continue;
|
||||
for (size_t i = 0; group->gr_mem[i]; ++i) {
|
||||
if (username == group->gr_mem[i]) {
|
||||
extra_gids.append(group->gr_gid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
endgrent();
|
||||
return extra_gids;
|
||||
}
|
||||
|
||||
ErrorOr<Account> Account::from_passwd(passwd const& pwd, spwd const& spwd)
|
||||
{
|
||||
Account account(pwd, spwd, get_extra_gids(pwd));
|
||||
endpwent();
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
endspent();
|
||||
#endif
|
||||
return account;
|
||||
}
|
||||
|
||||
ErrorOr<Account> Account::self([[maybe_unused]] Read options)
|
||||
{
|
||||
Vector<gid_t> extra_gids = TRY(Core::System::getgroups());
|
||||
|
||||
auto pwd = TRY(Core::System::getpwuid(getuid()));
|
||||
if (!pwd.has_value())
|
||||
return Error::from_string_literal("No such user");
|
||||
|
||||
spwd spwd = {};
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
if (options != Read::PasswdOnly) {
|
||||
auto maybe_spwd = TRY(Core::System::getspnam({ pwd->pw_name, strlen(pwd->pw_name) }));
|
||||
if (!maybe_spwd.has_value())
|
||||
return Error::from_string_literal("No shadow entry for user");
|
||||
spwd = maybe_spwd.release_value();
|
||||
}
|
||||
#endif
|
||||
|
||||
return Account(*pwd, spwd, extra_gids);
|
||||
}
|
||||
|
||||
ErrorOr<Account> Account::from_name(StringView username, [[maybe_unused]] Read options)
|
||||
{
|
||||
auto pwd = TRY(Core::System::getpwnam(username));
|
||||
if (!pwd.has_value())
|
||||
return Error::from_string_literal("No such user");
|
||||
|
||||
spwd spwd = {};
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
if (options != Read::PasswdOnly) {
|
||||
auto maybe_spwd = TRY(Core::System::getspnam({ pwd->pw_name, strlen(pwd->pw_name) }));
|
||||
if (!maybe_spwd.has_value())
|
||||
return Error::from_string_literal("No shadow entry for user");
|
||||
spwd = maybe_spwd.release_value();
|
||||
}
|
||||
#endif
|
||||
return from_passwd(*pwd, spwd);
|
||||
}
|
||||
|
||||
ErrorOr<Account> Account::from_uid(uid_t uid, [[maybe_unused]] Read options)
|
||||
{
|
||||
auto pwd = TRY(Core::System::getpwuid(uid));
|
||||
if (!pwd.has_value())
|
||||
return Error::from_string_literal("No such user");
|
||||
|
||||
spwd spwd = {};
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
if (options != Read::PasswdOnly) {
|
||||
auto maybe_spwd = TRY(Core::System::getspnam({ pwd->pw_name, strlen(pwd->pw_name) }));
|
||||
if (!maybe_spwd.has_value())
|
||||
return Error::from_string_literal("No shadow entry for user");
|
||||
spwd = maybe_spwd.release_value();
|
||||
}
|
||||
#endif
|
||||
return from_passwd(*pwd, spwd);
|
||||
}
|
||||
|
||||
ErrorOr<Vector<Account>> Account::all([[maybe_unused]] Read options)
|
||||
{
|
||||
Vector<Account> accounts;
|
||||
char buffer[1024] = { 0 };
|
||||
|
||||
ScopeGuard pwent_guard([] { endpwent(); });
|
||||
setpwent();
|
||||
|
||||
while (true) {
|
||||
auto pwd = TRY(Core::System::getpwent({ buffer, sizeof(buffer) }));
|
||||
if (!pwd.has_value())
|
||||
break;
|
||||
|
||||
spwd spwd = {};
|
||||
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
ScopeGuard spent_guard([] { endspent(); });
|
||||
if (options != Read::PasswdOnly) {
|
||||
auto maybe_spwd = TRY(Core::System::getspnam({ pwd->pw_name, strlen(pwd->pw_name) }));
|
||||
if (!maybe_spwd.has_value())
|
||||
return Error::from_string_literal("No shadow entry for user");
|
||||
spwd = maybe_spwd.release_value();
|
||||
}
|
||||
#endif
|
||||
|
||||
accounts.append({ *pwd, spwd, get_extra_gids(*pwd) });
|
||||
}
|
||||
|
||||
return accounts;
|
||||
}
|
||||
|
||||
bool Account::authenticate(SecretString const& password) const
|
||||
{
|
||||
// If there was no shadow entry for this account, authentication always fails.
|
||||
if (!m_password_hash.has_value())
|
||||
return false;
|
||||
|
||||
// An empty passwd field indicates that no password is required to log in.
|
||||
if (m_password_hash->is_empty())
|
||||
return true;
|
||||
|
||||
// FIXME: Use crypt_r if it can be built in lagom.
|
||||
auto const bytes = m_password_hash->characters();
|
||||
char* hash = crypt(password.characters(), bytes);
|
||||
return hash != nullptr && AK::timing_safe_compare(hash, bytes, m_password_hash->length());
|
||||
}
|
||||
|
||||
ErrorOr<void> Account::login() const
|
||||
{
|
||||
TRY(Core::System::setgroups(m_extra_gids));
|
||||
TRY(Core::System::setgid(m_gid));
|
||||
TRY(Core::System::setuid(m_uid));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
void Account::set_password(SecretString const& password)
|
||||
{
|
||||
m_password_hash = crypt(password.characters(), get_salt().characters());
|
||||
}
|
||||
|
||||
void Account::set_password_enabled(bool enabled)
|
||||
{
|
||||
auto flattened_password_hash = m_password_hash.value_or(ByteString::empty());
|
||||
if (enabled && !flattened_password_hash.is_empty() && flattened_password_hash[0] == '!') {
|
||||
m_password_hash = flattened_password_hash.substring(1, flattened_password_hash.length() - 1);
|
||||
} else if (!enabled && (flattened_password_hash.is_empty() || flattened_password_hash[0] != '!')) {
|
||||
StringBuilder builder;
|
||||
builder.append('!');
|
||||
builder.append(flattened_password_hash);
|
||||
m_password_hash = builder.to_byte_string();
|
||||
}
|
||||
}
|
||||
|
||||
void Account::delete_password()
|
||||
{
|
||||
m_password_hash = ByteString::empty();
|
||||
}
|
||||
|
||||
Account::Account(passwd const& pwd, spwd const& spwd, Vector<gid_t> extra_gids)
|
||||
: m_username(pwd.pw_name)
|
||||
, m_password_hash(spwd.sp_pwdp ? Optional<ByteString>(spwd.sp_pwdp) : OptionalNone {})
|
||||
, m_uid(pwd.pw_uid)
|
||||
, m_gid(pwd.pw_gid)
|
||||
, m_gecos(pwd.pw_gecos)
|
||||
, m_home_directory(pwd.pw_dir)
|
||||
, m_shell(pwd.pw_shell)
|
||||
, m_extra_gids(move(extra_gids))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<ByteString> Account::generate_passwd_file() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
char buffer[1024] = { 0 };
|
||||
|
||||
ScopeGuard pwent_guard([] { endpwent(); });
|
||||
setpwent();
|
||||
|
||||
while (true) {
|
||||
auto pwd = TRY(Core::System::getpwent({ buffer, sizeof(buffer) }));
|
||||
if (!pwd.has_value())
|
||||
break;
|
||||
|
||||
if (pwd->pw_name == m_username) {
|
||||
if (m_deleted)
|
||||
continue;
|
||||
builder.appendff("{}:!:{}:{}:{}:{}:{}\n",
|
||||
m_username,
|
||||
m_uid, m_gid,
|
||||
m_gecos,
|
||||
m_home_directory,
|
||||
m_shell);
|
||||
|
||||
} else {
|
||||
builder.appendff("{}:!:{}:{}:{}:{}:{}\n",
|
||||
pwd->pw_name, pwd->pw_uid,
|
||||
pwd->pw_gid, pwd->pw_gecos, pwd->pw_dir,
|
||||
pwd->pw_shell);
|
||||
}
|
||||
}
|
||||
|
||||
return builder.to_byte_string();
|
||||
}
|
||||
|
||||
ErrorOr<ByteString> Account::generate_group_file() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
char buffer[1024] = { 0 };
|
||||
|
||||
ScopeGuard pwent_guard([] { endgrent(); });
|
||||
setgrent();
|
||||
|
||||
while (true) {
|
||||
auto group = TRY(Core::System::getgrent(buffer));
|
||||
if (!group.has_value())
|
||||
break;
|
||||
|
||||
auto should_be_present = !m_deleted && m_extra_gids.contains_slow(group->gr_gid);
|
||||
|
||||
auto already_present = false;
|
||||
Vector<char const*> members;
|
||||
for (size_t i = 0; group->gr_mem[i]; ++i) {
|
||||
auto const* member = group->gr_mem[i];
|
||||
if (member == m_username) {
|
||||
already_present = true;
|
||||
if (!should_be_present)
|
||||
continue;
|
||||
}
|
||||
members.append(member);
|
||||
}
|
||||
|
||||
if (should_be_present && !already_present)
|
||||
members.append(m_username.characters());
|
||||
|
||||
builder.appendff("{}:{}:{}:{}\n", group->gr_name, group->gr_passwd, group->gr_gid, ByteString::join(","sv, members));
|
||||
}
|
||||
|
||||
return builder.to_byte_string();
|
||||
}
|
||||
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
ErrorOr<ByteString> Account::generate_shadow_file() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
|
||||
setspent();
|
||||
|
||||
struct spwd* p;
|
||||
errno = 0;
|
||||
while ((p = getspent())) {
|
||||
if (p->sp_namp == m_username) {
|
||||
if (m_deleted)
|
||||
continue;
|
||||
builder.appendff("{}:{}", m_username, m_password_hash.value_or(ByteString::empty()));
|
||||
} else
|
||||
builder.appendff("{}:{}", p->sp_namp, p->sp_pwdp);
|
||||
|
||||
builder.appendff(":{}:{}:{}:{}:{}:{}:{}\n",
|
||||
(p->sp_lstchg == -1) ? "" : ByteString::formatted("{}", p->sp_lstchg),
|
||||
(p->sp_min == -1) ? "" : ByteString::formatted("{}", p->sp_min),
|
||||
(p->sp_max == -1) ? "" : ByteString::formatted("{}", p->sp_max),
|
||||
(p->sp_warn == -1) ? "" : ByteString::formatted("{}", p->sp_warn),
|
||||
(p->sp_inact == -1) ? "" : ByteString::formatted("{}", p->sp_inact),
|
||||
(p->sp_expire == -1) ? "" : ByteString::formatted("{}", p->sp_expire),
|
||||
(p->sp_flag == 0) ? "" : ByteString::formatted("{}", p->sp_flag));
|
||||
}
|
||||
endspent();
|
||||
|
||||
if (errno)
|
||||
return Error::from_errno(errno);
|
||||
|
||||
return builder.to_byte_string();
|
||||
}
|
||||
#endif
|
||||
|
||||
ErrorOr<void> Account::sync()
|
||||
{
|
||||
Core::UmaskScope umask_scope(0777);
|
||||
|
||||
auto new_passwd_file_content = TRY(generate_passwd_file());
|
||||
auto new_group_file_content = TRY(generate_group_file());
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
auto new_shadow_file_content = TRY(generate_shadow_file());
|
||||
#endif
|
||||
|
||||
char new_passwd_file[] = "/etc/passwd.XXXXXX";
|
||||
char new_group_file[] = "/etc/group.XXXXXX";
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
char new_shadow_file[] = "/etc/shadow.XXXXXX";
|
||||
#endif
|
||||
|
||||
{
|
||||
auto new_passwd_fd = TRY(Core::System::mkstemp(new_passwd_file));
|
||||
ScopeGuard new_passwd_fd_guard = [new_passwd_fd] { close(new_passwd_fd); };
|
||||
TRY(Core::System::fchmod(new_passwd_fd, 0644));
|
||||
|
||||
auto new_group_fd = TRY(Core::System::mkstemp(new_group_file));
|
||||
ScopeGuard new_group_fd_guard = [new_group_fd] { close(new_group_fd); };
|
||||
TRY(Core::System::fchmod(new_group_fd, 0644));
|
||||
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
auto new_shadow_fd = TRY(Core::System::mkstemp(new_shadow_file));
|
||||
ScopeGuard new_shadow_fd_guard = [new_shadow_fd] { close(new_shadow_fd); };
|
||||
TRY(Core::System::fchmod(new_shadow_fd, 0600));
|
||||
#endif
|
||||
|
||||
auto nwritten = TRY(Core::System::write(new_passwd_fd, new_passwd_file_content.bytes()));
|
||||
VERIFY(static_cast<size_t>(nwritten) == new_passwd_file_content.length());
|
||||
|
||||
nwritten = TRY(Core::System::write(new_group_fd, new_group_file_content.bytes()));
|
||||
VERIFY(static_cast<size_t>(nwritten) == new_group_file_content.length());
|
||||
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
nwritten = TRY(Core::System::write(new_shadow_fd, new_shadow_file_content.bytes()));
|
||||
VERIFY(static_cast<size_t>(nwritten) == new_shadow_file_content.length());
|
||||
#endif
|
||||
}
|
||||
|
||||
auto new_passwd_file_view = StringView { new_passwd_file, sizeof(new_passwd_file) };
|
||||
TRY(Core::System::rename(new_passwd_file_view, "/etc/passwd"sv));
|
||||
|
||||
auto new_group_file_view = StringView { new_group_file, sizeof(new_group_file) };
|
||||
TRY(Core::System::rename(new_group_file_view, "/etc/group"sv));
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
auto new_shadow_file_view = StringView { new_shadow_file, sizeof(new_shadow_file) };
|
||||
TRY(Core::System::rename(new_shadow_file_view, "/etc/shadow"sv));
|
||||
#endif
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
|
@ -1,95 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Peter Elliott <pelliott@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Types.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibCore/SecretString.h>
|
||||
#include <pwd.h>
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
# include <shadow.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
#ifdef AK_OS_BSD_GENERIC
|
||||
struct spwd {
|
||||
char* sp_namp;
|
||||
char* sp_pwdp;
|
||||
};
|
||||
#endif
|
||||
|
||||
class Account {
|
||||
public:
|
||||
enum class Read {
|
||||
All,
|
||||
PasswdOnly
|
||||
};
|
||||
|
||||
static ErrorOr<Account> self(Read options = Read::All);
|
||||
static ErrorOr<Account> from_name(StringView username, Read options = Read::All);
|
||||
static ErrorOr<Account> from_uid(uid_t uid, Read options = Read::All);
|
||||
static ErrorOr<Vector<Account>> all(Read options = Read::All);
|
||||
|
||||
bool authenticate(SecretString const& password) const;
|
||||
ErrorOr<void> login() const;
|
||||
|
||||
ByteString username() const { return m_username; }
|
||||
ByteString password_hash() const { return m_password_hash.value_or(ByteString::empty()); }
|
||||
|
||||
// Setters only affect in-memory copy of password.
|
||||
// You must call sync to apply changes.
|
||||
void set_password(SecretString const& password);
|
||||
void set_password_enabled(bool enabled);
|
||||
void set_home_directory(StringView home_directory) { m_home_directory = home_directory; }
|
||||
void set_uid(uid_t uid) { m_uid = uid; }
|
||||
void set_gid(gid_t gid) { m_gid = gid; }
|
||||
void set_shell(StringView shell) { m_shell = shell; }
|
||||
void set_gecos(StringView gecos) { m_gecos = gecos; }
|
||||
void set_deleted() { m_deleted = true; }
|
||||
void set_extra_gids(Vector<gid_t> extra_gids) { m_extra_gids = move(extra_gids); }
|
||||
void delete_password();
|
||||
|
||||
// A nonexistent password means that this account was missing from /etc/shadow.
|
||||
// It's considered to have a password in that case, and authentication will always fail.
|
||||
bool has_password() const { return !m_password_hash.has_value() || !m_password_hash->is_empty(); }
|
||||
|
||||
uid_t uid() const { return m_uid; }
|
||||
gid_t gid() const { return m_gid; }
|
||||
ByteString const& gecos() const { return m_gecos; }
|
||||
ByteString const& home_directory() const { return m_home_directory; }
|
||||
ByteString const& shell() const { return m_shell; }
|
||||
Vector<gid_t> const& extra_gids() const { return m_extra_gids; }
|
||||
|
||||
ErrorOr<void> sync();
|
||||
|
||||
private:
|
||||
static ErrorOr<Account> from_passwd(passwd const&, spwd const&);
|
||||
|
||||
Account(passwd const& pwd, spwd const& spwd, Vector<gid_t> extra_gids);
|
||||
|
||||
ErrorOr<ByteString> generate_passwd_file() const;
|
||||
ErrorOr<ByteString> generate_group_file() const;
|
||||
#ifndef AK_OS_BSD_GENERIC
|
||||
ErrorOr<ByteString> generate_shadow_file() const;
|
||||
#endif
|
||||
|
||||
ByteString m_username;
|
||||
|
||||
Optional<ByteString> m_password_hash;
|
||||
uid_t m_uid { 0 };
|
||||
gid_t m_gid { 0 };
|
||||
ByteString m_gecos;
|
||||
ByteString m_home_directory;
|
||||
ByteString m_shell;
|
||||
Vector<gid_t> m_extra_gids;
|
||||
bool m_deleted { false };
|
||||
};
|
||||
|
||||
}
|
|
@ -31,33 +31,21 @@ set(SOURCES
|
|||
EventLoopImplementation.cpp
|
||||
EventLoopImplementationUnix.cpp
|
||||
EventReceiver.cpp
|
||||
LockFile.cpp
|
||||
MappedFile.cpp
|
||||
MimeData.cpp
|
||||
Notifier.cpp
|
||||
Process.cpp
|
||||
ProcessStatisticsReader.cpp
|
||||
Resource.cpp
|
||||
ResourceImplementation.cpp
|
||||
ResourceImplementationFile.cpp
|
||||
SecretString.cpp
|
||||
SessionManagement.cpp
|
||||
Socket.cpp
|
||||
SOCKSProxyClient.cpp
|
||||
SystemServerTakeover.cpp
|
||||
TCPServer.cpp
|
||||
ThreadEventQueue.cpp
|
||||
Timer.cpp
|
||||
UDPServer.cpp
|
||||
)
|
||||
if (NOT ANDROID AND NOT WIN32 AND NOT EMSCRIPTEN)
|
||||
list(APPEND SOURCES
|
||||
Account.cpp
|
||||
FilePermissionsMask.cpp
|
||||
GetPassword.cpp
|
||||
Group.cpp
|
||||
)
|
||||
endif()
|
||||
if (NOT WIN32 AND NOT EMSCRIPTEN)
|
||||
list(APPEND SOURCES LocalServer.cpp)
|
||||
endif()
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, MacDue <macdue@dueutil.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibCore/Timer.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
template<typename TFunction>
|
||||
auto debounce(int timeout, TFunction function)
|
||||
{
|
||||
RefPtr<Core::Timer> timer;
|
||||
return [=]<typename... T>(T... args) mutable {
|
||||
auto apply_function = [=] { function(args...); };
|
||||
if (timer) {
|
||||
timer->stop();
|
||||
timer->on_timeout = move(apply_function);
|
||||
} else {
|
||||
timer = Core::Timer::create_single_shot(timeout, move(apply_function));
|
||||
}
|
||||
timer->start();
|
||||
};
|
||||
}
|
||||
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Xavier Defrang <xavier.defrang@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/CharacterTypes.h>
|
||||
#include <AK/StringUtils.h>
|
||||
|
||||
#include <LibCore/FilePermissionsMask.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
enum State {
|
||||
Classes,
|
||||
Mode
|
||||
};
|
||||
|
||||
enum ClassFlag {
|
||||
Other = 1,
|
||||
Group = 2,
|
||||
User = 4,
|
||||
All = 7
|
||||
};
|
||||
|
||||
enum Operation {
|
||||
Add,
|
||||
Remove,
|
||||
Assign,
|
||||
};
|
||||
|
||||
ErrorOr<FilePermissionsMask> FilePermissionsMask::parse(StringView string)
|
||||
{
|
||||
return (!string.is_empty() && is_ascii_digit(string[0]))
|
||||
? from_numeric_notation(string)
|
||||
: from_symbolic_notation(string);
|
||||
}
|
||||
|
||||
ErrorOr<FilePermissionsMask> FilePermissionsMask::from_numeric_notation(StringView string)
|
||||
{
|
||||
string = string.trim_whitespace();
|
||||
mode_t mode = AK::StringUtils::convert_to_uint_from_octal<u16>(string, TrimWhitespace::No).value_or(010000);
|
||||
if (mode > 07777)
|
||||
return Error::from_string_literal("invalid octal representation");
|
||||
|
||||
FilePermissionsMask mask;
|
||||
mask.assign_permissions(mode);
|
||||
|
||||
// For compatibility purposes, just clear the special mode bits if we explicitly passed a 4-character mode.
|
||||
if (string.length() >= 4)
|
||||
mask.remove_permissions(07000);
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
ErrorOr<FilePermissionsMask> FilePermissionsMask::from_symbolic_notation(StringView string)
|
||||
{
|
||||
auto mask = FilePermissionsMask();
|
||||
|
||||
u8 state = State::Classes;
|
||||
u8 classes = 0;
|
||||
u8 operation = 0;
|
||||
|
||||
for (auto ch : string) {
|
||||
switch (state) {
|
||||
case State::Classes: {
|
||||
// zero or more [ugoa] terminated by one operator [+-=]
|
||||
if (ch == 'u')
|
||||
classes |= ClassFlag::User;
|
||||
else if (ch == 'g')
|
||||
classes |= ClassFlag::Group;
|
||||
else if (ch == 'o')
|
||||
classes |= ClassFlag::Other;
|
||||
else if (ch == 'a')
|
||||
classes = ClassFlag::All;
|
||||
else {
|
||||
if (ch == '+')
|
||||
operation = Operation::Add;
|
||||
else if (ch == '-')
|
||||
operation = Operation::Remove;
|
||||
else if (ch == '=')
|
||||
operation = Operation::Assign;
|
||||
else if (classes == 0)
|
||||
return Error::from_string_literal("invalid class: expected 'u', 'g', 'o' or 'a'");
|
||||
else
|
||||
return Error::from_string_literal("invalid operation: expected '+', '-' or '='");
|
||||
|
||||
// if an operation was specified without a class, assume all
|
||||
if (classes == 0)
|
||||
classes = ClassFlag::All;
|
||||
|
||||
state = State::Mode;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case State::Mode: {
|
||||
// one or more [rwx] terminated by a comma
|
||||
|
||||
// End of mode part, expect class next
|
||||
if (ch == ',') {
|
||||
state = State::Classes;
|
||||
classes = operation = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
mode_t write_bits = 0;
|
||||
bool apply_to_directories_and_executables_only = false;
|
||||
|
||||
switch (ch) {
|
||||
case 'r':
|
||||
write_bits = 4;
|
||||
break;
|
||||
case 'w':
|
||||
write_bits = 2;
|
||||
break;
|
||||
case 'x':
|
||||
write_bits = 1;
|
||||
break;
|
||||
case 'X':
|
||||
write_bits = 1;
|
||||
apply_to_directories_and_executables_only = true;
|
||||
break;
|
||||
default:
|
||||
return Error::from_string_literal("invalid symbolic permission: expected 'r', 'w' or 'x'");
|
||||
}
|
||||
|
||||
mode_t clear_bits = operation == Operation::Assign ? 7 : write_bits;
|
||||
|
||||
FilePermissionsMask& edit_mask = apply_to_directories_and_executables_only ? mask.directory_or_executable_mask() : mask;
|
||||
|
||||
// Update masks one class at a time in other, group, user order
|
||||
for (auto cls = classes; cls != 0; cls >>= 1) {
|
||||
if (cls & 1) {
|
||||
if (operation == Operation::Add || operation == Operation::Assign)
|
||||
edit_mask.add_permissions(write_bits);
|
||||
if (operation == Operation::Remove || operation == Operation::Assign)
|
||||
edit_mask.remove_permissions(clear_bits);
|
||||
}
|
||||
write_bits <<= 3;
|
||||
clear_bits <<= 3;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
FilePermissionsMask& FilePermissionsMask::assign_permissions(mode_t mode)
|
||||
{
|
||||
m_write_mask = mode;
|
||||
m_clear_mask = 0777;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FilePermissionsMask& FilePermissionsMask::add_permissions(mode_t mode)
|
||||
{
|
||||
m_write_mask |= mode;
|
||||
return *this;
|
||||
}
|
||||
|
||||
FilePermissionsMask& FilePermissionsMask::remove_permissions(mode_t mode)
|
||||
{
|
||||
m_clear_mask |= mode;
|
||||
return *this;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Xavier Defrang <xavier.defrang@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class FilePermissionsMask {
|
||||
public:
|
||||
static ErrorOr<FilePermissionsMask> parse(StringView string);
|
||||
static ErrorOr<FilePermissionsMask> from_numeric_notation(StringView string);
|
||||
static ErrorOr<FilePermissionsMask> from_symbolic_notation(StringView string);
|
||||
|
||||
FilePermissionsMask()
|
||||
: m_clear_mask(0)
|
||||
, m_write_mask(0)
|
||||
{
|
||||
}
|
||||
|
||||
FilePermissionsMask& assign_permissions(mode_t mode);
|
||||
FilePermissionsMask& add_permissions(mode_t mode);
|
||||
FilePermissionsMask& remove_permissions(mode_t mode);
|
||||
|
||||
mode_t apply(mode_t mode) const
|
||||
{
|
||||
if (m_directory_or_executable_mask && (S_ISDIR(mode) || (mode & 0111) != 0))
|
||||
mode = m_directory_or_executable_mask->apply(mode);
|
||||
|
||||
return m_write_mask | (mode & ~m_clear_mask);
|
||||
}
|
||||
mode_t clear_mask() const { return m_clear_mask; }
|
||||
mode_t write_mask() const { return m_write_mask; }
|
||||
|
||||
FilePermissionsMask& directory_or_executable_mask()
|
||||
{
|
||||
if (!m_directory_or_executable_mask)
|
||||
m_directory_or_executable_mask = make<FilePermissionsMask>();
|
||||
|
||||
return *m_directory_or_executable_mask;
|
||||
}
|
||||
|
||||
private:
|
||||
mode_t m_clear_mask; // the bits that will be cleared
|
||||
mode_t m_write_mask; // the bits that will be set
|
||||
|
||||
// A separate mask, only for files that already have some executable bit set or directories.
|
||||
OwnPtr<FilePermissionsMask> m_directory_or_executable_mask;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Peter Elliott <pelliott@serenityos.org>
|
||||
* Copyright (c) 2021, Emanuele Torre <torreemanuele6@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCore/GetPassword.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <stdio.h>
|
||||
#include <termios.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
ErrorOr<SecretString> get_password(StringView prompt)
|
||||
{
|
||||
TRY(Core::System::write(STDOUT_FILENO, prompt.bytes()));
|
||||
|
||||
auto original = TRY(Core::System::tcgetattr(STDIN_FILENO));
|
||||
|
||||
termios no_echo = original;
|
||||
no_echo.c_lflag &= ~ECHO;
|
||||
TRY(Core::System::tcsetattr(STDIN_FILENO, TCSAFLUSH, no_echo));
|
||||
|
||||
char* password = nullptr;
|
||||
size_t n = 0;
|
||||
|
||||
auto line_length = getline(&password, &n, stdin);
|
||||
auto saved_errno = errno;
|
||||
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &original);
|
||||
putchar('\n');
|
||||
|
||||
if (line_length < 0)
|
||||
return Error::from_errno(saved_errno);
|
||||
|
||||
VERIFY(line_length != 0);
|
||||
|
||||
// Remove trailing '\n' read by getline().
|
||||
password[line_length - 1] = '\0';
|
||||
|
||||
return TRY(SecretString::take_ownership(password, line_length));
|
||||
}
|
||||
}
|
|
@ -1,16 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2020, Peter Elliott <pelliott@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Error.h>
|
||||
#include <LibCore/SecretString.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
ErrorOr<SecretString> get_password(StringView prompt = "Password: "sv);
|
||||
|
||||
}
|
|
@ -1,187 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/CharacterTypes.h>
|
||||
#include <AK/ScopeGuard.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibCore/Group.h>
|
||||
#include <LibCore/System.h>
|
||||
#include <LibCore/UmaskScope.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
ErrorOr<ByteString> Group::generate_group_file() const
|
||||
{
|
||||
StringBuilder builder;
|
||||
char buffer[1024] = { 0 };
|
||||
|
||||
ScopeGuard grent_guard([] { endgrent(); });
|
||||
setgrent();
|
||||
|
||||
while (true) {
|
||||
auto group = TRY(Core::System::getgrent({ buffer, sizeof(buffer) }));
|
||||
if (!group.has_value())
|
||||
break;
|
||||
|
||||
if (group->gr_name == m_name)
|
||||
builder.appendff("{}:x:{}:{}\n", m_name, m_id, ByteString::join(',', m_members));
|
||||
else {
|
||||
Vector<ByteString> members;
|
||||
if (group->gr_mem) {
|
||||
for (size_t i = 0; group->gr_mem[i]; ++i)
|
||||
members.append(group->gr_mem[i]);
|
||||
}
|
||||
|
||||
builder.appendff("{}:x:{}:{}\n", group->gr_name, group->gr_gid, ByteString::join(',', members));
|
||||
}
|
||||
}
|
||||
|
||||
return builder.to_byte_string();
|
||||
}
|
||||
|
||||
ErrorOr<void> Group::sync()
|
||||
{
|
||||
Core::UmaskScope umask_scope(0777);
|
||||
|
||||
auto new_group_file_content = TRY(generate_group_file());
|
||||
|
||||
char new_group_file[] = "/etc/group.XXXXXX";
|
||||
auto new_group_file_view = StringView { new_group_file, sizeof(new_group_file) };
|
||||
|
||||
{
|
||||
auto new_group_fd = TRY(Core::System::mkstemp(new_group_file));
|
||||
ScopeGuard new_group_fd_guard([new_group_fd] { close(new_group_fd); });
|
||||
TRY(Core::System::fchmod(new_group_fd, 0664));
|
||||
|
||||
auto nwritten = TRY(Core::System::write(new_group_fd, new_group_file_content.bytes()));
|
||||
VERIFY(static_cast<size_t>(nwritten) == new_group_file_content.length());
|
||||
}
|
||||
|
||||
TRY(Core::System::rename(new_group_file_view, "/etc/group"sv));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_ANDROID) && !defined(AK_OS_HAIKU)
|
||||
ErrorOr<void> Group::add_group(Group& group)
|
||||
{
|
||||
if (group.name().is_empty())
|
||||
return Error::from_string_literal("Group name can not be empty.");
|
||||
|
||||
// A quick sanity check on group name
|
||||
if (group.name().find_any_of("\\/!@#$%^&*()~+=`:\n"sv, ByteString::SearchDirection::Forward).has_value())
|
||||
return Error::from_string_literal("Group name has invalid characters.");
|
||||
|
||||
// Disallow names starting with '_', '-' or other non-alpha characters.
|
||||
if (group.name().starts_with('_') || group.name().starts_with('-') || !is_ascii_alpha(group.name().characters()[0]))
|
||||
return Error::from_string_literal("Group name has invalid characters.");
|
||||
|
||||
// Verify group name does not already exist
|
||||
if (TRY(name_exists(group.name())))
|
||||
return Error::from_string_literal("Group name already exists.");
|
||||
|
||||
// Sort out the group id for the group
|
||||
if (group.id() > 0) {
|
||||
if (TRY(id_exists(group.id())))
|
||||
return Error::from_string_literal("Group ID already exists.");
|
||||
} else {
|
||||
gid_t group_id = 100;
|
||||
while (true) {
|
||||
if (!TRY(id_exists(group_id)))
|
||||
break;
|
||||
group_id++;
|
||||
}
|
||||
group.set_group_id(group_id);
|
||||
}
|
||||
|
||||
auto gr = TRY(group.to_libc_group());
|
||||
|
||||
FILE* file = fopen("/etc/group", "a");
|
||||
if (!file)
|
||||
return Error::from_errno(errno);
|
||||
|
||||
ScopeGuard file_guard { [&] {
|
||||
fclose(file);
|
||||
} };
|
||||
|
||||
if (putgrent(&gr, file) < 0)
|
||||
return Error::from_errno(errno);
|
||||
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
ErrorOr<Vector<Group>> Group::all()
|
||||
{
|
||||
Vector<Group> groups;
|
||||
char buffer[1024] = { 0 };
|
||||
|
||||
ScopeGuard grent_guard([] { endgrent(); });
|
||||
setgrent();
|
||||
|
||||
while (true) {
|
||||
auto group = TRY(Core::System::getgrent({ buffer, sizeof(buffer) }));
|
||||
if (!group.has_value())
|
||||
break;
|
||||
|
||||
Vector<ByteString> members;
|
||||
if (group->gr_mem) {
|
||||
for (size_t i = 0; group->gr_mem[i]; ++i)
|
||||
members.append(group->gr_mem[i]);
|
||||
}
|
||||
|
||||
groups.append({ group->gr_name, group->gr_gid, move(members) });
|
||||
}
|
||||
|
||||
return groups;
|
||||
}
|
||||
|
||||
Group::Group(ByteString name, gid_t id, Vector<ByteString> members)
|
||||
: m_name(move(name))
|
||||
, m_id(id)
|
||||
, m_members(move(members))
|
||||
{
|
||||
}
|
||||
|
||||
ErrorOr<bool> Group::name_exists(StringView name)
|
||||
{
|
||||
return TRY(Core::System::getgrnam(name)).has_value();
|
||||
}
|
||||
|
||||
ErrorOr<bool> Group::id_exists(gid_t id)
|
||||
{
|
||||
return TRY(Core::System::getgrgid(id)).has_value();
|
||||
}
|
||||
|
||||
// NOTE: struct group returned from this function cannot outlive an instance of Group.
|
||||
ErrorOr<struct group> Group::to_libc_group()
|
||||
{
|
||||
struct group gr;
|
||||
gr.gr_name = const_cast<char*>(m_name.characters());
|
||||
gr.gr_passwd = const_cast<char*>("x");
|
||||
gr.gr_gid = m_id;
|
||||
gr.gr_mem = nullptr;
|
||||
|
||||
// FIXME: A better solution would surely be not using a static here
|
||||
// NOTE: This now means that there cannot be multiple struct groups at the same time, because only one gr.gr_mem can ever be valid at the same time.
|
||||
// NOTE: Not using a static here would result in gr.gr_mem being freed up on exit from this function.
|
||||
static Vector<char*> members;
|
||||
members.clear_with_capacity();
|
||||
if (m_members.size() > 0) {
|
||||
TRY(members.try_ensure_capacity(m_members.size() + 1));
|
||||
for (auto member : m_members)
|
||||
members.unchecked_append(const_cast<char*>(member.characters()));
|
||||
members.unchecked_append(nullptr);
|
||||
|
||||
gr.gr_mem = const_cast<char**>(members.data());
|
||||
}
|
||||
|
||||
return gr;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Kenneth Myhra <kennethmyhra@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Error.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <grp.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class Group {
|
||||
public:
|
||||
#if !defined(AK_OS_BSD_GENERIC) && !defined(AK_OS_ANDROID) && !defined(AK_OS_HAIKU)
|
||||
static ErrorOr<void> add_group(Group& group);
|
||||
#endif
|
||||
|
||||
static ErrorOr<Vector<Group>> all();
|
||||
|
||||
Group() = default;
|
||||
Group(ByteString name, gid_t id = 0, Vector<ByteString> members = {});
|
||||
|
||||
~Group() = default;
|
||||
|
||||
ByteString const& name() const { return m_name; }
|
||||
void set_name(ByteString const& name) { m_name = name; }
|
||||
|
||||
gid_t id() const { return m_id; }
|
||||
void set_group_id(gid_t const id) { m_id = id; }
|
||||
|
||||
Vector<ByteString>& members() { return m_members; }
|
||||
|
||||
ErrorOr<void> sync();
|
||||
|
||||
private:
|
||||
static ErrorOr<bool> name_exists(StringView name);
|
||||
static ErrorOr<bool> id_exists(gid_t id);
|
||||
ErrorOr<struct group> to_libc_group();
|
||||
|
||||
ErrorOr<ByteString> generate_group_file() const;
|
||||
|
||||
ByteString m_name;
|
||||
gid_t m_id { 0 };
|
||||
Vector<ByteString> m_members;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,57 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Peter Elliott <pelliott@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibCore/Directory.h>
|
||||
#include <LibCore/LockFile.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/file.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
LockFile::LockFile(char const* filename, Type type)
|
||||
: m_filename(filename)
|
||||
{
|
||||
if (Core::Directory::create(LexicalPath(m_filename).parent(), Core::Directory::CreateDirectories::Yes).is_error())
|
||||
return;
|
||||
|
||||
m_fd = open(filename, O_RDONLY | O_CREAT | O_CLOEXEC, 0666);
|
||||
if (m_fd == -1) {
|
||||
m_errno = errno;
|
||||
return;
|
||||
}
|
||||
|
||||
if (flock(m_fd, LOCK_NB | ((type == Type::Exclusive) ? LOCK_EX : LOCK_SH)) == -1) {
|
||||
m_errno = errno;
|
||||
close(m_fd);
|
||||
m_fd = -1;
|
||||
}
|
||||
}
|
||||
|
||||
LockFile::~LockFile()
|
||||
{
|
||||
release();
|
||||
}
|
||||
|
||||
bool LockFile::is_held() const
|
||||
{
|
||||
return m_fd != -1;
|
||||
}
|
||||
|
||||
void LockFile::release()
|
||||
{
|
||||
if (m_fd == -1)
|
||||
return;
|
||||
|
||||
unlink(m_filename);
|
||||
flock(m_fd, LOCK_NB | LOCK_UN);
|
||||
close(m_fd);
|
||||
|
||||
m_fd = -1;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Peter Elliott <pelliott@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Core {
|
||||
|
||||
class LockFile {
|
||||
public:
|
||||
enum class Type {
|
||||
Exclusive,
|
||||
Shared
|
||||
};
|
||||
|
||||
LockFile(LockFile const& other) = delete;
|
||||
LockFile(char const* filename, Type type = Type::Exclusive);
|
||||
~LockFile();
|
||||
|
||||
bool is_held() const;
|
||||
int error_code() const { return m_errno; }
|
||||
void release();
|
||||
|
||||
private:
|
||||
int m_fd { -1 };
|
||||
int m_errno { 0 };
|
||||
char const* m_filename { nullptr };
|
||||
};
|
||||
|
||||
}
|
|
@ -1,112 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/JsonArray.h>
|
||||
#include <AK/JsonObject.h>
|
||||
#include <AK/JsonValue.h>
|
||||
#include <LibCore/File.h>
|
||||
#include <LibCore/ProcessStatisticsReader.h>
|
||||
#include <pwd.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
HashMap<uid_t, ByteString> ProcessStatisticsReader::s_usernames;
|
||||
|
||||
ErrorOr<AllProcessesStatistics> ProcessStatisticsReader::get_all(SeekableStream& proc_all_file, bool include_usernames)
|
||||
{
|
||||
TRY(proc_all_file.seek(0, SeekMode::SetPosition));
|
||||
|
||||
AllProcessesStatistics all_processes_statistics;
|
||||
|
||||
auto file_contents = TRY(proc_all_file.read_until_eof());
|
||||
auto json_obj = TRY(JsonValue::from_string(file_contents)).as_object();
|
||||
json_obj.get_array("processes"sv)->for_each([&](auto& value) {
|
||||
JsonObject const& process_object = value.as_object();
|
||||
Core::ProcessStatistics process;
|
||||
|
||||
// kernel data first
|
||||
process.pid = process_object.get_u32("pid"sv).value_or(0);
|
||||
process.pgid = process_object.get_u32("pgid"sv).value_or(0);
|
||||
process.pgp = process_object.get_u32("pgp"sv).value_or(0);
|
||||
process.sid = process_object.get_u32("sid"sv).value_or(0);
|
||||
process.uid = process_object.get_u32("uid"sv).value_or(0);
|
||||
process.gid = process_object.get_u32("gid"sv).value_or(0);
|
||||
process.ppid = process_object.get_u32("ppid"sv).value_or(0);
|
||||
process.kernel = process_object.get_bool("kernel"sv).value_or(false);
|
||||
process.name = process_object.get_byte_string("name"sv).value_or("");
|
||||
process.executable = process_object.get_byte_string("executable"sv).value_or("");
|
||||
process.tty = process_object.get_byte_string("tty"sv).value_or("");
|
||||
process.pledge = process_object.get_byte_string("pledge"sv).value_or("");
|
||||
process.veil = process_object.get_byte_string("veil"sv).value_or("");
|
||||
process.creation_time = UnixDateTime::from_nanoseconds_since_epoch(process_object.get_i64("creation_time"sv).value_or(0));
|
||||
process.amount_virtual = process_object.get_u32("amount_virtual"sv).value_or(0);
|
||||
process.amount_resident = process_object.get_u32("amount_resident"sv).value_or(0);
|
||||
process.amount_shared = process_object.get_u32("amount_shared"sv).value_or(0);
|
||||
process.amount_dirty_private = process_object.get_u32("amount_dirty_private"sv).value_or(0);
|
||||
process.amount_clean_inode = process_object.get_u32("amount_clean_inode"sv).value_or(0);
|
||||
process.amount_purgeable_volatile = process_object.get_u32("amount_purgeable_volatile"sv).value_or(0);
|
||||
process.amount_purgeable_nonvolatile = process_object.get_u32("amount_purgeable_nonvolatile"sv).value_or(0);
|
||||
|
||||
auto& thread_array = process_object.get_array("threads"sv).value();
|
||||
process.threads.ensure_capacity(thread_array.size());
|
||||
thread_array.for_each([&](auto& value) {
|
||||
auto& thread_object = value.as_object();
|
||||
Core::ThreadStatistics thread;
|
||||
thread.tid = thread_object.get_u32("tid"sv).value_or(0);
|
||||
thread.times_scheduled = thread_object.get_u32("times_scheduled"sv).value_or(0);
|
||||
thread.name = thread_object.get_byte_string("name"sv).value_or("");
|
||||
thread.state = thread_object.get_byte_string("state"sv).value_or("");
|
||||
thread.time_user = thread_object.get_u64("time_user"sv).value_or(0);
|
||||
thread.time_kernel = thread_object.get_u64("time_kernel"sv).value_or(0);
|
||||
thread.cpu = thread_object.get_u32("cpu"sv).value_or(0);
|
||||
thread.priority = thread_object.get_u32("priority"sv).value_or(0);
|
||||
thread.syscall_count = thread_object.get_u32("syscall_count"sv).value_or(0);
|
||||
thread.inode_faults = thread_object.get_u32("inode_faults"sv).value_or(0);
|
||||
thread.zero_faults = thread_object.get_u32("zero_faults"sv).value_or(0);
|
||||
thread.cow_faults = thread_object.get_u32("cow_faults"sv).value_or(0);
|
||||
thread.unix_socket_read_bytes = thread_object.get_u64("unix_socket_read_bytes"sv).value_or(0);
|
||||
thread.unix_socket_write_bytes = thread_object.get_u64("unix_socket_write_bytes"sv).value_or(0);
|
||||
thread.ipv4_socket_read_bytes = thread_object.get_u64("ipv4_socket_read_bytes"sv).value_or(0);
|
||||
thread.ipv4_socket_write_bytes = thread_object.get_u64("ipv4_socket_write_bytes"sv).value_or(0);
|
||||
thread.file_read_bytes = thread_object.get_u64("file_read_bytes"sv).value_or(0);
|
||||
thread.file_write_bytes = thread_object.get_u64("file_write_bytes"sv).value_or(0);
|
||||
process.threads.append(move(thread));
|
||||
});
|
||||
|
||||
// and synthetic data last
|
||||
if (include_usernames) {
|
||||
process.username = username_from_uid(process.uid);
|
||||
}
|
||||
all_processes_statistics.processes.append(move(process));
|
||||
});
|
||||
|
||||
all_processes_statistics.total_time_scheduled = json_obj.get_u64("total_time"sv).value_or(0);
|
||||
all_processes_statistics.total_time_scheduled_kernel = json_obj.get_u64("total_time_kernel"sv).value_or(0);
|
||||
return all_processes_statistics;
|
||||
}
|
||||
|
||||
ErrorOr<AllProcessesStatistics> ProcessStatisticsReader::get_all(bool include_usernames)
|
||||
{
|
||||
auto proc_all_file = TRY(Core::File::open("/sys/kernel/processes"sv, Core::File::OpenMode::Read));
|
||||
return get_all(*proc_all_file, include_usernames);
|
||||
}
|
||||
|
||||
ByteString ProcessStatisticsReader::username_from_uid(uid_t uid)
|
||||
{
|
||||
if (s_usernames.is_empty()) {
|
||||
setpwent();
|
||||
while (auto* passwd = getpwent())
|
||||
s_usernames.set(passwd->pw_uid, passwd->pw_name);
|
||||
endpwent();
|
||||
}
|
||||
|
||||
auto it = s_usernames.find(uid);
|
||||
if (it != s_usernames.end())
|
||||
return (*it).value;
|
||||
return ByteString::number(uid);
|
||||
}
|
||||
}
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2018-2021, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Time.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
struct ThreadStatistics {
|
||||
pid_t tid;
|
||||
unsigned times_scheduled;
|
||||
u64 time_user;
|
||||
u64 time_kernel;
|
||||
unsigned syscall_count;
|
||||
unsigned inode_faults;
|
||||
unsigned zero_faults;
|
||||
unsigned cow_faults;
|
||||
u64 unix_socket_read_bytes;
|
||||
u64 unix_socket_write_bytes;
|
||||
u64 ipv4_socket_read_bytes;
|
||||
u64 ipv4_socket_write_bytes;
|
||||
u64 file_read_bytes;
|
||||
u64 file_write_bytes;
|
||||
ByteString state;
|
||||
u32 cpu;
|
||||
u32 priority;
|
||||
ByteString name;
|
||||
};
|
||||
|
||||
struct ProcessStatistics {
|
||||
// Keep this in sync with /sys/kernel/processes.
|
||||
// From the kernel side:
|
||||
pid_t pid;
|
||||
pid_t pgid;
|
||||
pid_t pgp;
|
||||
pid_t sid;
|
||||
uid_t uid;
|
||||
gid_t gid;
|
||||
pid_t ppid;
|
||||
bool kernel;
|
||||
ByteString name;
|
||||
ByteString executable;
|
||||
ByteString tty;
|
||||
ByteString pledge;
|
||||
ByteString veil;
|
||||
UnixDateTime creation_time;
|
||||
size_t amount_virtual;
|
||||
size_t amount_resident;
|
||||
size_t amount_shared;
|
||||
size_t amount_dirty_private;
|
||||
size_t amount_clean_inode;
|
||||
size_t amount_purgeable_volatile;
|
||||
size_t amount_purgeable_nonvolatile;
|
||||
|
||||
Vector<Core::ThreadStatistics> threads;
|
||||
|
||||
// synthetic
|
||||
ByteString username;
|
||||
};
|
||||
|
||||
struct AllProcessesStatistics {
|
||||
Vector<ProcessStatistics> processes;
|
||||
u64 total_time_scheduled;
|
||||
u64 total_time_scheduled_kernel;
|
||||
};
|
||||
|
||||
class ProcessStatisticsReader {
|
||||
public:
|
||||
static ErrorOr<AllProcessesStatistics> get_all(SeekableStream&, bool include_usernames = true);
|
||||
static ErrorOr<AllProcessesStatistics> get_all(bool include_usernames = true);
|
||||
|
||||
private:
|
||||
static ByteString username_from_uid(uid_t);
|
||||
static HashMap<uid_t, ByteString> s_usernames;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,311 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/MemoryStream.h>
|
||||
#include <LibCore/SOCKSProxyClient.h>
|
||||
|
||||
enum class Method : u8 {
|
||||
NoAuth = 0x00,
|
||||
GSSAPI = 0x01,
|
||||
UsernamePassword = 0x02,
|
||||
NoAcceptableMethods = 0xFF,
|
||||
};
|
||||
|
||||
enum class AddressType : u8 {
|
||||
IPV4 = 0x01,
|
||||
DomainName = 0x03,
|
||||
IPV6 = 0x04,
|
||||
};
|
||||
|
||||
enum class Reply {
|
||||
Succeeded = 0x00,
|
||||
GeneralSocksServerFailure = 0x01,
|
||||
ConnectionNotAllowedByRuleset = 0x02,
|
||||
NetworkUnreachable = 0x03,
|
||||
HostUnreachable = 0x04,
|
||||
ConnectionRefused = 0x05,
|
||||
TTLExpired = 0x06,
|
||||
CommandNotSupported = 0x07,
|
||||
AddressTypeNotSupported = 0x08,
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Socks5VersionIdentifierAndMethodSelectionMessage {
|
||||
u8 version_identifier;
|
||||
u8 method_count;
|
||||
// NOTE: We only send a single method, so we don't need to make this variable-length.
|
||||
u8 methods[1];
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Socks5VersionIdentifierAndMethodSelectionMessage> : public AK::DefaultTraits<Socks5VersionIdentifierAndMethodSelectionMessage> {
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Socks5InitialResponse {
|
||||
u8 version_identifier;
|
||||
u8 method;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Socks5InitialResponse> : public AK::DefaultTraits<Socks5InitialResponse> {
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Socks5ConnectRequestHeader {
|
||||
u8 version_identifier;
|
||||
u8 command;
|
||||
u8 reserved;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Socks5ConnectRequestHeader> : public AK::DefaultTraits<Socks5ConnectRequestHeader> {
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Socks5ConnectRequestTrailer {
|
||||
u16 port;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Socks5ConnectRequestTrailer> : public AK::DefaultTraits<Socks5ConnectRequestTrailer> {
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Socks5ConnectResponseHeader {
|
||||
u8 version_identifier;
|
||||
u8 status;
|
||||
u8 reserved;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Socks5ConnectResponseHeader> : public AK::DefaultTraits<Socks5ConnectResponseHeader> {
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Socks5ConnectResponseTrailer {
|
||||
u8 bind_port;
|
||||
};
|
||||
|
||||
struct [[gnu::packed]] Socks5UsernamePasswordResponse {
|
||||
u8 version_identifier;
|
||||
u8 status;
|
||||
};
|
||||
|
||||
template<>
|
||||
struct AK::Traits<Socks5UsernamePasswordResponse> : public AK::DefaultTraits<Socks5UsernamePasswordResponse> {
|
||||
static constexpr bool is_trivially_serializable() { return true; }
|
||||
};
|
||||
|
||||
namespace {
|
||||
StringView reply_response_name(Reply reply)
|
||||
{
|
||||
switch (reply) {
|
||||
case Reply::Succeeded:
|
||||
return "Succeeded"sv;
|
||||
case Reply::GeneralSocksServerFailure:
|
||||
return "GeneralSocksServerFailure"sv;
|
||||
case Reply::ConnectionNotAllowedByRuleset:
|
||||
return "ConnectionNotAllowedByRuleset"sv;
|
||||
case Reply::NetworkUnreachable:
|
||||
return "NetworkUnreachable"sv;
|
||||
case Reply::HostUnreachable:
|
||||
return "HostUnreachable"sv;
|
||||
case Reply::ConnectionRefused:
|
||||
return "ConnectionRefused"sv;
|
||||
case Reply::TTLExpired:
|
||||
return "TTLExpired"sv;
|
||||
case Reply::CommandNotSupported:
|
||||
return "CommandNotSupported"sv;
|
||||
case Reply::AddressTypeNotSupported:
|
||||
return "AddressTypeNotSupported"sv;
|
||||
}
|
||||
VERIFY_NOT_REACHED();
|
||||
}
|
||||
|
||||
ErrorOr<void> send_version_identifier_and_method_selection_message(Core::Socket& socket, Core::SOCKSProxyClient::Version version, Method method)
|
||||
{
|
||||
Socks5VersionIdentifierAndMethodSelectionMessage message {
|
||||
.version_identifier = to_underlying(version),
|
||||
.method_count = 1,
|
||||
.methods = { to_underlying(method) },
|
||||
};
|
||||
TRY(socket.write_value(message));
|
||||
|
||||
auto response = TRY(socket.read_value<Socks5InitialResponse>());
|
||||
|
||||
if (response.version_identifier != to_underlying(version))
|
||||
return Error::from_string_literal("SOCKS negotiation failed: Invalid version identifier");
|
||||
|
||||
if (response.method != to_underlying(method))
|
||||
return Error::from_string_literal("SOCKS negotiation failed: Failed to negotiate a method");
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ErrorOr<Reply> send_connect_request_message(Core::Socket& socket, Core::SOCKSProxyClient::Version version, Core::SOCKSProxyClient::HostOrIPV4 target, int port, Core::SOCKSProxyClient::Command command)
|
||||
{
|
||||
AllocatingMemoryStream stream;
|
||||
|
||||
Socks5ConnectRequestHeader header {
|
||||
.version_identifier = to_underlying(version),
|
||||
.command = to_underlying(command),
|
||||
.reserved = 0,
|
||||
};
|
||||
Socks5ConnectRequestTrailer trailer {
|
||||
.port = htons(port),
|
||||
};
|
||||
|
||||
TRY(stream.write_value(header));
|
||||
|
||||
TRY(target.visit(
|
||||
[&](ByteString const& hostname) -> ErrorOr<void> {
|
||||
u8 address_data[2];
|
||||
address_data[0] = to_underlying(AddressType::DomainName);
|
||||
address_data[1] = hostname.length();
|
||||
TRY(stream.write_until_depleted({ address_data, sizeof(address_data) }));
|
||||
TRY(stream.write_until_depleted({ hostname.characters(), hostname.length() }));
|
||||
return {};
|
||||
},
|
||||
[&](u32 ipv4) -> ErrorOr<void> {
|
||||
u8 address_data[5];
|
||||
address_data[0] = to_underlying(AddressType::IPV4);
|
||||
u32 network_ordered_ipv4 = NetworkOrdered<u32>(ipv4);
|
||||
memcpy(address_data + 1, &network_ordered_ipv4, sizeof(network_ordered_ipv4));
|
||||
TRY(stream.write_until_depleted({ address_data, sizeof(address_data) }));
|
||||
return {};
|
||||
}));
|
||||
|
||||
TRY(stream.write_value(trailer));
|
||||
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(stream.used_buffer_size()));
|
||||
TRY(stream.read_until_filled(buffer.bytes()));
|
||||
TRY(socket.write_until_depleted(buffer));
|
||||
|
||||
auto response_header = TRY(socket.read_value<Socks5ConnectResponseHeader>());
|
||||
|
||||
if (response_header.version_identifier != to_underlying(version))
|
||||
return Error::from_string_literal("SOCKS negotiation failed: Invalid version identifier");
|
||||
|
||||
auto response_address_type = TRY(socket.read_value<u8>());
|
||||
|
||||
switch (AddressType(response_address_type)) {
|
||||
case AddressType::IPV4: {
|
||||
u8 response_address_data[4];
|
||||
TRY(socket.read_until_filled({ response_address_data, sizeof(response_address_data) }));
|
||||
break;
|
||||
}
|
||||
case AddressType::DomainName: {
|
||||
auto response_address_length = TRY(socket.read_value<u8>());
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(response_address_length));
|
||||
TRY(socket.read_until_filled(buffer));
|
||||
break;
|
||||
}
|
||||
case AddressType::IPV6:
|
||||
default:
|
||||
return Error::from_string_literal("SOCKS negotiation failed: Invalid connect response address type");
|
||||
}
|
||||
|
||||
[[maybe_unused]] auto bound_port = TRY(socket.read_value<u16>());
|
||||
|
||||
return Reply(response_header.status);
|
||||
}
|
||||
|
||||
ErrorOr<u8> send_username_password_authentication_message(Core::Socket& socket, Core::SOCKSProxyClient::UsernamePasswordAuthenticationData const& auth_data)
|
||||
{
|
||||
AllocatingMemoryStream stream;
|
||||
|
||||
u8 version = 0x01;
|
||||
TRY(stream.write_value(version));
|
||||
|
||||
u8 username_length = auth_data.username.length();
|
||||
TRY(stream.write_value(username_length));
|
||||
|
||||
TRY(stream.write_until_depleted({ auth_data.username.characters(), auth_data.username.length() }));
|
||||
|
||||
u8 password_length = auth_data.password.length();
|
||||
TRY(stream.write_value(password_length));
|
||||
|
||||
TRY(stream.write_until_depleted({ auth_data.password.characters(), auth_data.password.length() }));
|
||||
|
||||
auto buffer = TRY(ByteBuffer::create_uninitialized(stream.used_buffer_size()));
|
||||
TRY(stream.read_until_filled(buffer.bytes()));
|
||||
|
||||
TRY(socket.write_until_depleted(buffer));
|
||||
|
||||
auto response = TRY(socket.read_value<Socks5UsernamePasswordResponse>());
|
||||
|
||||
if (response.version_identifier != version)
|
||||
return Error::from_string_literal("SOCKS negotiation failed: Invalid version identifier");
|
||||
|
||||
return response.status;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Core {
|
||||
|
||||
SOCKSProxyClient::~SOCKSProxyClient()
|
||||
{
|
||||
close();
|
||||
m_socket.on_ready_to_read = nullptr;
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<SOCKSProxyClient>> SOCKSProxyClient::connect(Socket& underlying, Version version, HostOrIPV4 const& target, int target_port, Variant<UsernamePasswordAuthenticationData, Empty> const& auth_data, Command command)
|
||||
{
|
||||
if (version != Version::V5)
|
||||
return Error::from_string_literal("SOCKS version not supported");
|
||||
|
||||
return auth_data.visit(
|
||||
[&](Empty) -> ErrorOr<NonnullOwnPtr<SOCKSProxyClient>> {
|
||||
TRY(send_version_identifier_and_method_selection_message(underlying, version, Method::NoAuth));
|
||||
auto reply = TRY(send_connect_request_message(underlying, version, target, target_port, command));
|
||||
if (reply != Reply::Succeeded) {
|
||||
underlying.close();
|
||||
return Error::from_string_view(reply_response_name(reply));
|
||||
}
|
||||
|
||||
return adopt_nonnull_own_or_enomem(new SOCKSProxyClient {
|
||||
underlying,
|
||||
nullptr,
|
||||
});
|
||||
},
|
||||
[&](UsernamePasswordAuthenticationData const& auth_data) -> ErrorOr<NonnullOwnPtr<SOCKSProxyClient>> {
|
||||
TRY(send_version_identifier_and_method_selection_message(underlying, version, Method::UsernamePassword));
|
||||
auto auth_response = TRY(send_username_password_authentication_message(underlying, auth_data));
|
||||
if (auth_response != 0) {
|
||||
underlying.close();
|
||||
return Error::from_string_literal("SOCKS authentication failed");
|
||||
}
|
||||
|
||||
auto reply = TRY(send_connect_request_message(underlying, version, target, target_port, command));
|
||||
if (reply != Reply::Succeeded) {
|
||||
underlying.close();
|
||||
return Error::from_string_view(reply_response_name(reply));
|
||||
}
|
||||
|
||||
return adopt_nonnull_own_or_enomem(new SOCKSProxyClient {
|
||||
underlying,
|
||||
nullptr,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ErrorOr<NonnullOwnPtr<SOCKSProxyClient>> SOCKSProxyClient::connect(HostOrIPV4 const& server, int server_port, Version version, HostOrIPV4 const& target, int target_port, Variant<UsernamePasswordAuthenticationData, Empty> const& auth_data, Command command)
|
||||
{
|
||||
auto underlying = TRY(server.visit(
|
||||
[&](u32 ipv4) {
|
||||
return Core::TCPSocket::connect({ IPv4Address(ipv4), static_cast<u16>(server_port) });
|
||||
},
|
||||
[&](ByteString const& hostname) {
|
||||
return Core::TCPSocket::connect(hostname, static_cast<u16>(server_port));
|
||||
}));
|
||||
|
||||
auto socket = TRY(connect(*underlying, version, target, target_port, auth_data, command));
|
||||
socket->m_own_underlying_socket = move(underlying);
|
||||
dbgln("SOCKS proxy connected, have {} available bytes", TRY(socket->m_socket.pending_bytes()));
|
||||
return socket;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,64 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Ali Mohammad Pur <mpfard@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibCore/Proxy.h>
|
||||
#include <LibCore/Socket.h>
|
||||
|
||||
namespace Core {
|
||||
class SOCKSProxyClient final : public Socket {
|
||||
public:
|
||||
enum class Version : u8 {
|
||||
V4 = 0x04,
|
||||
V5 = 0x05,
|
||||
};
|
||||
|
||||
struct UsernamePasswordAuthenticationData {
|
||||
ByteString username;
|
||||
ByteString password;
|
||||
};
|
||||
|
||||
enum class Command : u8 {
|
||||
Connect = 0x01,
|
||||
Bind = 0x02,
|
||||
UDPAssociate = 0x03,
|
||||
};
|
||||
|
||||
using HostOrIPV4 = Variant<ByteString, u32>;
|
||||
|
||||
static ErrorOr<NonnullOwnPtr<SOCKSProxyClient>> connect(Socket& underlying, Version, HostOrIPV4 const& target, int target_port, Variant<UsernamePasswordAuthenticationData, Empty> const& auth_data = {}, Command = Command::Connect);
|
||||
static ErrorOr<NonnullOwnPtr<SOCKSProxyClient>> connect(HostOrIPV4 const& server, int server_port, Version, HostOrIPV4 const& target, int target_port, Variant<UsernamePasswordAuthenticationData, Empty> const& auth_data = {}, Command = Command::Connect);
|
||||
|
||||
virtual ~SOCKSProxyClient() override;
|
||||
|
||||
// ^Stream::Stream
|
||||
virtual ErrorOr<Bytes> read_some(Bytes bytes) override { return m_socket.read_some(bytes); }
|
||||
virtual ErrorOr<size_t> write_some(ReadonlyBytes bytes) override { return m_socket.write_some(bytes); }
|
||||
virtual bool is_eof() const override { return m_socket.is_eof(); }
|
||||
virtual bool is_open() const override { return m_socket.is_open(); }
|
||||
virtual void close() override { m_socket.close(); }
|
||||
|
||||
// ^Stream::Socket
|
||||
virtual ErrorOr<size_t> pending_bytes() const override { return m_socket.pending_bytes(); }
|
||||
virtual ErrorOr<bool> can_read_without_blocking(int timeout = 0) const override { return m_socket.can_read_without_blocking(timeout); }
|
||||
virtual ErrorOr<void> set_blocking(bool enabled) override { return m_socket.set_blocking(enabled); }
|
||||
virtual ErrorOr<void> set_close_on_exec(bool enabled) override { return m_socket.set_close_on_exec(enabled); }
|
||||
virtual void set_notifications_enabled(bool enabled) override { m_socket.set_notifications_enabled(enabled); }
|
||||
|
||||
private:
|
||||
SOCKSProxyClient(Socket& socket, OwnPtr<Socket> own_socket)
|
||||
: m_socket(socket)
|
||||
, m_own_underlying_socket(move(own_socket))
|
||||
{
|
||||
m_socket.on_ready_to_read = [this] { on_ready_to_read(); };
|
||||
}
|
||||
|
||||
Socket& m_socket;
|
||||
OwnPtr<Socket> m_own_underlying_socket;
|
||||
};
|
||||
}
|
|
@ -1,50 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
* Copyright (c) 2021, Mustafa Quraish <mustafa@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Memory.h>
|
||||
#include <LibCore/SecretString.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
ErrorOr<SecretString> SecretString::take_ownership(char*& cstring, size_t length)
|
||||
{
|
||||
auto buffer = TRY(ByteBuffer::copy(cstring, length));
|
||||
|
||||
secure_zero(cstring, length);
|
||||
free(cstring);
|
||||
cstring = nullptr;
|
||||
|
||||
return SecretString(move(buffer));
|
||||
}
|
||||
|
||||
SecretString SecretString::take_ownership(ByteBuffer&& buffer)
|
||||
{
|
||||
return SecretString(move(buffer));
|
||||
}
|
||||
|
||||
SecretString::SecretString(ByteBuffer&& buffer)
|
||||
: m_secure_buffer(move(buffer))
|
||||
{
|
||||
// SecretString is currently only used to provide the character data to invocations to crypt(),
|
||||
// which requires a NUL-terminated string. To ensure this operation avoids a buffer overrun,
|
||||
// append a NUL terminator here if there isn't already one.
|
||||
if (m_secure_buffer.is_empty() || (m_secure_buffer[m_secure_buffer.size() - 1] != 0)) {
|
||||
u8 nul = '\0';
|
||||
m_secure_buffer.append(&nul, 1);
|
||||
}
|
||||
}
|
||||
|
||||
SecretString::~SecretString()
|
||||
{
|
||||
// Note: We use secure_zero to avoid the zeroing from being optimized out by the compiler,
|
||||
// which is possible if memset was to be used here.
|
||||
if (!m_secure_buffer.is_empty()) {
|
||||
secure_zero(m_secure_buffer.data(), m_secure_buffer.capacity());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2021, Brian Gianforcaro <bgianf@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteBuffer.h>
|
||||
#include <AK/Noncopyable.h>
|
||||
#include <AK/StringView.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class SecretString {
|
||||
AK_MAKE_NONCOPYABLE(SecretString);
|
||||
AK_MAKE_DEFAULT_MOVABLE(SecretString);
|
||||
|
||||
public:
|
||||
[[nodiscard]] static ErrorOr<SecretString> take_ownership(char*&, size_t);
|
||||
[[nodiscard]] static SecretString take_ownership(ByteBuffer&&);
|
||||
|
||||
[[nodiscard]] bool is_empty() const { return m_secure_buffer.is_empty(); }
|
||||
[[nodiscard]] size_t length() const { return m_secure_buffer.size(); }
|
||||
[[nodiscard]] char const* characters() const { return reinterpret_cast<char const*>(m_secure_buffer.data()); }
|
||||
[[nodiscard]] StringView view() const { return { characters(), length() }; }
|
||||
|
||||
SecretString() = default;
|
||||
~SecretString();
|
||||
|
||||
private:
|
||||
explicit SecretString(ByteBuffer&&);
|
||||
|
||||
ByteBuffer m_secure_buffer;
|
||||
};
|
||||
|
||||
}
|
|
@ -1,30 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
namespace Core {
|
||||
|
||||
class UmaskScope {
|
||||
public:
|
||||
explicit UmaskScope(mode_t mask)
|
||||
{
|
||||
m_old_mask = umask(mask);
|
||||
}
|
||||
|
||||
~UmaskScope()
|
||||
{
|
||||
umask(m_old_mask);
|
||||
}
|
||||
|
||||
private:
|
||||
mode_t m_old_mask {};
|
||||
};
|
||||
|
||||
}
|
Loading…
Reference in a new issue