summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGunnar Beutner <gbeutner@serenityos.org>2021-05-01 11:43:38 +0200
committerAndreas Kling <kling@serenityos.org>2021-05-01 12:40:12 +0200
commit302f9798eebc3b0b80ea08aa3506959b249e4155 (patch)
tree46f2cae69da5485b70cf52c280333f848cadcf19
parentce77caf479a8f7003d3789737a888cec628efae3 (diff)
downloadserenity-302f9798eebc3b0b80ea08aa3506959b249e4155.zip
LibC: Implement support for getspnam() and friends
-rw-r--r--Base/etc/shadow14
-rw-r--r--Userland/Libraries/LibC/CMakeLists.txt1
-rw-r--r--Userland/Libraries/LibC/shadow.cpp248
-rw-r--r--Userland/Libraries/LibC/shadow.h39
-rw-r--r--Userland/Libraries/LibCore/Account.cpp97
-rw-r--r--Userland/Libraries/LibCore/Account.h12
-rw-r--r--Userland/Utilities/passwd.cpp4
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;
}