diff options
author | Gunnar Beutner <gbeutner@serenityos.org> | 2021-05-01 11:43:38 +0200 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2021-05-01 12:40:12 +0200 |
commit | 302f9798eebc3b0b80ea08aa3506959b249e4155 (patch) | |
tree | 46f2cae69da5485b70cf52c280333f848cadcf19 | |
parent | ce77caf479a8f7003d3789737a888cec628efae3 (diff) | |
download | serenity-302f9798eebc3b0b80ea08aa3506959b249e4155.zip |
LibC: Implement support for getspnam() and friends
-rw-r--r-- | Base/etc/shadow | 14 | ||||
-rw-r--r-- | Userland/Libraries/LibC/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Libraries/LibC/shadow.cpp | 248 | ||||
-rw-r--r-- | Userland/Libraries/LibC/shadow.h | 39 | ||||
-rw-r--r-- | Userland/Libraries/LibCore/Account.cpp | 97 | ||||
-rw-r--r-- | Userland/Libraries/LibCore/Account.h | 12 | ||||
-rw-r--r-- | Userland/Utilities/passwd.cpp | 4 |
7 files changed, 355 insertions, 60 deletions
diff --git a/Base/etc/shadow b/Base/etc/shadow index 2a96727ce7..77bde03556 100644 --- a/Base/etc/shadow +++ b/Base/etc/shadow @@ -1,2 +1,12 @@ -root: -anon:$5$zFiQBeTD88m/mhbU$ecHDSdRd5yNV45BzIRXwtRpxJtMpVI5twjRRXO8X03Q= +root::18727:::::: +anon:$5$zFiQBeTD88m/mhbU$ecHDSdRd5yNV45BzIRXwtRpxJtMpVI5twjRRXO8X03Q=:18727:0:99999:::: +lookup:!*:18727:::::: +request:!*:18727:::::: +notify:!*:18727:::::: +window:!*:18727:::::: +clipboard:!*:18727:::::: +webcontent:!*:18727:::::: +image:!*:18727:::::: +symbol:!*:18727:::::: +websocket:!*:18727:::::: +nona:!*:18727:::::: diff --git a/Userland/Libraries/LibC/CMakeLists.txt b/Userland/Libraries/LibC/CMakeLists.txt index b84282ee33..d29b9cf22c 100644 --- a/Userland/Libraries/LibC/CMakeLists.txt +++ b/Userland/Libraries/LibC/CMakeLists.txt @@ -29,6 +29,7 @@ set(LIBC_SOURCES scanf.cpp sched.cpp serenity.cpp + shadow.cpp signal.cpp spawn.cpp stat.cpp diff --git a/Userland/Libraries/LibC/shadow.cpp b/Userland/Libraries/LibC/shadow.cpp new file mode 100644 index 0000000000..0aa5cae1f9 --- /dev/null +++ b/Userland/Libraries/LibC/shadow.cpp @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> + * Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <AK/String.h> +#include <AK/TemporaryChange.h> +#include <AK/Vector.h> +#include <errno_numbers.h> +#include <shadow.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +extern "C" { + +static FILE* s_stream = nullptr; +static unsigned s_line_number = 0; +static struct spwd s_shadow_entry; + +static String s_name; +static String s_pwdp; + +void setspent() +{ + s_line_number = 0; + if (s_stream) { + rewind(s_stream); + } else { + s_stream = fopen("/etc/shadow", "r"); + if (!s_stream) { + perror("open /etc/shadow"); + } + } +} + +void endspent() +{ + s_line_number = 0; + if (s_stream) { + fclose(s_stream); + s_stream = nullptr; + } + + memset(&s_shadow_entry, 0, sizeof(s_shadow_entry)); + + s_name = {}; + s_pwdp = {}; +} + +struct spwd* getspnam(const char* name) +{ + setspent(); + while (auto* sp = getspent()) { + if (!strcmp(sp->sp_namp, name)) { + return sp; + } + } + return nullptr; +} + +static bool parse_shadow_entry(const String& line) +{ + auto parts = line.split_view(':', true); + if (parts.size() != 9) { + dbgln("getspent(): Malformed entry on line {}", s_line_number); + return false; + } + + s_name = parts[0]; + s_pwdp = parts[1]; + auto& lstchg_string = parts[2]; + auto& min_string = parts[3]; + auto& max_string = parts[4]; + auto& warn_string = parts[5]; + auto& inact_string = parts[6]; + auto& expire_string = parts[7]; + auto& flag_string = parts[8]; + + auto lstchg = lstchg_string.to_int(); + if (!lstchg.has_value()) { + dbgln("getspent(): Malformed lstchg on line {}", s_line_number); + return false; + } + + if (min_string.is_empty()) + min_string = "-1"; + auto min_value = min_string.to_int(); + if (!min_value.has_value()) { + dbgln("getspent(): Malformed min value on line {}", s_line_number); + return false; + } + + if (max_string.is_empty()) + max_string = "-1"; + auto max_value = max_string.to_int(); + if (!max_value.has_value()) { + dbgln("getspent(): Malformed max value on line {}", s_line_number); + return false; + } + + if (warn_string.is_empty()) + warn_string = "-1"; + auto warn = warn_string.to_int(); + if (!warn.has_value()) { + dbgln("getspent(): Malformed warn on line {}", s_line_number); + return false; + } + + if (inact_string.is_empty()) + inact_string = "-1"; + auto inact = inact_string.to_int(); + if (!inact.has_value()) { + dbgln("getspent(): Malformed inact on line {}", s_line_number); + return false; + } + + if (expire_string.is_empty()) + expire_string = "-1"; + auto expire = expire_string.to_int(); + if (!expire.has_value()) { + dbgln("getspent(): Malformed expire on line {}", s_line_number); + return false; + } + + if (flag_string.is_empty()) + flag_string = "0"; + auto flag = flag_string.to_int(); + if (!flag.has_value()) { + dbgln("getspent(): Malformed flag on line {}", s_line_number); + return false; + } + + s_shadow_entry.sp_namp = const_cast<char*>(s_name.characters()); + s_shadow_entry.sp_pwdp = const_cast<char*>(s_pwdp.characters()); + s_shadow_entry.sp_lstchg = lstchg.value(); + s_shadow_entry.sp_min = min_value.value(); + s_shadow_entry.sp_max = max_value.value(); + s_shadow_entry.sp_warn = warn.value(); + s_shadow_entry.sp_inact = inact.value(); + s_shadow_entry.sp_expire = expire.value(); + s_shadow_entry.sp_flag = flag.value(); + + return true; +} + +struct spwd* getspent() +{ + if (!s_stream) + setspent(); + + while (true) { + if (!s_stream || feof(s_stream)) + return nullptr; + + if (ferror(s_stream)) { + dbgln("getspent(): Read error: {}", strerror(ferror(s_stream))); + return nullptr; + } + + char buffer[1024]; + ++s_line_number; + char* s = fgets(buffer, sizeof(buffer), s_stream); + + // Silently tolerate an empty line at the end. + if ((!s || !s[0]) && feof(s_stream)) + return nullptr; + + String line(s, Chomp); + if (parse_shadow_entry(line)) + return &s_shadow_entry; + // Otherwise, proceed to the next line. + } +} + +static void construct_spwd(struct spwd* sp, char* buf, struct spwd** result) +{ + auto* buf_name = &buf[0]; + auto* buf_pwdp = &buf[s_name.length() + 1]; + + bool ok = true; + ok = ok && s_name.copy_characters_to_buffer(buf_name, s_name.length() + 1); + ok = ok && s_pwdp.copy_characters_to_buffer(buf_pwdp, s_pwdp.length() + 1); + + VERIFY(ok); + + *result = sp; + sp->sp_namp = buf_name; + sp->sp_pwdp = buf_pwdp; +} + +int getspnam_r(const char* name, struct spwd* sp, char* buf, size_t buflen, struct spwd** result) +{ + // FIXME: This is a HACK! + TemporaryChange name_change { s_name, {} }; + TemporaryChange pwdp_change { s_pwdp, {} }; + + setspent(); + bool found = false; + while (auto* sp = getspent()) { + if (!strcmp(sp->sp_namp, name)) { + found = true; + break; + } + } + + if (!found) { + *result = nullptr; + return 0; + } + + const auto total_buffer_length = s_name.length() + s_pwdp.length() + 8; + if (buflen < total_buffer_length) + return ERANGE; + + construct_spwd(sp, buf, result); + return 0; +} + +int putspent(struct spwd* p, FILE* stream) +{ + if (!p || !stream || !p->sp_namp || !p->sp_pwdp) { + errno = EINVAL; + return -1; + } + + auto is_valid_field = [](const char* str) { + return str && !strpbrk(str, ":\n"); + }; + + if (!is_valid_field(p->sp_namp) || !is_valid_field(p->sp_pwdp)) { + errno = EINVAL; + return -1; + } + + int nwritten = fprintf(stream, "%s:%s:%ld:%ld:%ld:%ld:%ld:%ld:%ld\n", p->sp_namp, + p->sp_pwdp, p->sp_lstchg, p->sp_min, p->sp_max, p->sp_warn, p->sp_inact, p->sp_expire, p->sp_flag); + if (!nwritten || nwritten < 0) { + errno = ferror(stream); + return -1; + } + + return 0; +} +} diff --git a/Userland/Libraries/LibC/shadow.h b/Userland/Libraries/LibC/shadow.h new file mode 100644 index 0000000000..3c77ee5ee7 --- /dev/null +++ b/Userland/Libraries/LibC/shadow.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021, Gunnar Beutner <gbeutner@serenityos.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include <bits/FILE.h> +#include <sys/cdefs.h> +#include <sys/types.h> + +__BEGIN_DECLS + +struct spwd { + char* sp_namp; + char* sp_pwdp; + long int sp_lstchg; + long int sp_min; + long int sp_max; + long int sp_warn; + long int sp_inact; + long int sp_expire; + unsigned long int sp_flag; +}; + +struct spwd* getspent(); +void setspent(); +void endspent(); +struct spwd* getspnam(const char* name); +int putspent(struct spwd* p, FILE* stream); + +int getspent_r(struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp); +int getspnam_r(const char* name, struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp); + +int fgetspent_r(FILE* fp, struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp); +int sgetspent_r(const char* s, struct spwd* spbuf, char* buf, size_t buflen, struct spwd** spbufp); + +__END_DECLS diff --git a/Userland/Libraries/LibCore/Account.cpp b/Userland/Libraries/LibCore/Account.cpp index 869958c604..c86fb1d6f0 100644 --- a/Userland/Libraries/LibCore/Account.cpp +++ b/Userland/Libraries/LibCore/Account.cpp @@ -13,6 +13,7 @@ #include <errno.h> #include <grp.h> #include <pwd.h> +#include <shadow.h> #include <stdio.h> #include <string.h> #include <sys/stat.h> @@ -47,39 +48,52 @@ static Vector<gid_t> get_gids(const StringView& username) return extra_gids; } -Result<Account, String> Account::from_passwd(const passwd& pwd) +Result<Account, String> Account::from_passwd(const passwd& pwd, const spwd& spwd) { - Account account(pwd, get_gids(pwd.pw_name)); + Account account(pwd, spwd, get_gids(pwd.pw_name)); endpwent(); + endspent(); return account; } Result<Account, String> Account::from_name(const char* username) { - struct passwd* pwd = nullptr; errno = 0; - pwd = getpwnam(username); + auto* pwd = getpwnam(username); if (!pwd) { if (errno == 0) return String("No such user"); return String(strerror(errno)); } - return from_passwd(*pwd); + auto* spwd = getspnam(username); + if (!spwd) { + if (errno == 0) + return String("No such user"); + + return String(strerror(errno)); + } + return from_passwd(*pwd, *spwd); } Result<Account, String> Account::from_uid(uid_t uid) { - struct passwd* pwd = nullptr; errno = 0; - pwd = getpwuid(uid); + auto* pwd = getpwuid(uid); if (!pwd) { if (errno == 0) return String("No such user"); return String(strerror(errno)); } - return from_passwd(*pwd); + auto* spwd = getspnam(pwd->pw_name); + if (!spwd) { + if (errno == 0) + return String("No such user"); + + return String(strerror(errno)); + } + return from_passwd(*pwd, *spwd); } bool Account::authenticate(const char* password) const @@ -133,8 +147,9 @@ void Account::delete_password() m_password_hash = ""; } -Account::Account(const passwd& pwd, Vector<gid_t> extra_gids) +Account::Account(const passwd& pwd, const spwd& spwd, Vector<gid_t> extra_gids) : m_username(pwd.pw_name) + , m_password_hash(spwd.sp_pwdp) , m_uid(pwd.pw_uid) , m_gid(pwd.pw_gid) , m_gecos(pwd.pw_gecos) @@ -142,7 +157,6 @@ Account::Account(const passwd& pwd, Vector<gid_t> extra_gids) , m_shell(pwd.pw_shell) , m_extra_gids(extra_gids) { - load_shadow_file(); } String Account::generate_passwd_file() const @@ -179,50 +193,39 @@ String Account::generate_passwd_file() const return builder.to_string(); } -void Account::load_shadow_file() +String Account::generate_shadow_file() const { - auto file_or_error = Core::File::open("/etc/shadow", Core::File::ReadOnly); - VERIFY(!file_or_error.is_error()); - auto shadow_file = file_or_error.release_value(); - VERIFY(shadow_file->is_open()); - - Vector<ShadowEntry> entries; - - for (;;) { - auto line = shadow_file->read_line(); - if (line.is_null()) - break; - auto parts = line.split(':', true); - if (parts.size() != 2) { - dbgln("Malformed shadow entry, ignoring."); - continue; - } - const auto& username = parts[0]; - const auto& password_hash = parts[1]; - entries.append({ username, password_hash }); + StringBuilder builder; - if (username == m_username) { - m_password_hash = password_hash; - } - } + setspent(); - m_shadow_entries = move(entries); -} + struct spwd* p; + errno = 0; + while ((p = getspent())) { + if (p->sp_namp == m_username) { + builder.appendff("{}:{}:{}:{}:{}:{}:{}:{}:{}\n", + m_username, m_password_hash, + p->sp_lstchg, p->sp_min, + p->sp_max, p->sp_warn, + p->sp_inact, p->sp_expire, + p->sp_flag); -String Account::generate_shadow_file() const -{ - StringBuilder builder; - bool updated_entry_in_place = false; - for (auto& entry : m_shadow_entries) { - if (entry.username == m_username) { - updated_entry_in_place = true; - builder.appendff("{}:{}\n", m_username, m_password_hash); } else { - builder.appendff("{}:{}\n", entry.username, entry.password_hash); + builder.appendff("{}:{}:{}:{}:{}:{}:{}:{}:{}\n", + p->sp_namp, p->sp_pwdp, + p->sp_lstchg, p->sp_min, + p->sp_max, p->sp_warn, + p->sp_inact, p->sp_expire, + p->sp_flag); } } - if (!updated_entry_in_place) - builder.appendff("{}:{}\n", m_username, m_password_hash); + endspent(); + + if (errno) { + dbgln("errno was non-zero after generating new passwd file."); + return {}; + } + return builder.to_string(); } diff --git a/Userland/Libraries/LibCore/Account.h b/Userland/Libraries/LibCore/Account.h index b9231f10e9..c97dae424b 100644 --- a/Userland/Libraries/LibCore/Account.h +++ b/Userland/Libraries/LibCore/Account.h @@ -11,6 +11,7 @@ #include <AK/Types.h> #include <AK/Vector.h> #include <pwd.h> +#include <shadow.h> #include <sys/types.h> namespace Core { @@ -46,10 +47,9 @@ public: bool sync(); private: - static Result<Account, String> from_passwd(const passwd&); + static Result<Account, String> from_passwd(const passwd&, const spwd&); - Account(const passwd& pwd, Vector<gid_t> extra_gids); - void load_shadow_file(); + Account(const passwd& pwd, const spwd& spwd, Vector<gid_t> extra_gids); String generate_passwd_file() const; String generate_shadow_file() const; @@ -63,12 +63,6 @@ private: String m_home_directory; String m_shell; Vector<gid_t> m_extra_gids; - - struct ShadowEntry { - String username; - String password_hash; - }; - Vector<ShadowEntry> m_shadow_entries; }; } diff --git a/Userland/Utilities/passwd.cpp b/Userland/Utilities/passwd.cpp index 789f2e5219..48df09c0c3 100644 --- a/Userland/Utilities/passwd.cpp +++ b/Userland/Utilities/passwd.cpp @@ -64,7 +64,7 @@ int main(int argc, char** argv) setpwent(); - if (pledge("stdio wpath cpath fattr tty", nullptr) < 0) { + if (pledge("stdio wpath rpath cpath fattr tty", nullptr) < 0) { perror("pledge"); return 1; } @@ -93,7 +93,7 @@ int main(int argc, char** argv) target_account.set_password(new_password.value().characters()); } - if (pledge("stdio wpath cpath fattr", nullptr) < 0) { + if (pledge("stdio wpath rpath cpath fattr", nullptr) < 0) { perror("pledge"); return 1; } |