diff --git a/libc/config/linux/aarch64/headers.txt b/libc/config/linux/aarch64/headers.txt index 7d3e65c2434c7..b413e43f21446 100644 --- a/libc/config/linux/aarch64/headers.txt +++ b/libc/config/linux/aarch64/headers.txt @@ -30,6 +30,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.netinet_udp libc.include.poll libc.include.pthread + libc.include.pwd libc.include.sched libc.include.search libc.include.setjmp diff --git a/libc/config/linux/arm/headers.txt b/libc/config/linux/arm/headers.txt index eff8ea665d0c8..42580bfb10b2a 100644 --- a/libc/config/linux/arm/headers.txt +++ b/libc/config/linux/arm/headers.txt @@ -20,6 +20,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.stdlib libc.include.string libc.include.strings + libc.include.pwd libc.include.uchar libc.include.wchar libc.include.wctype diff --git a/libc/config/linux/i386/headers.txt b/libc/config/linux/i386/headers.txt index def05189f136d..1235db6e0b119 100644 --- a/libc/config/linux/i386/headers.txt +++ b/libc/config/linux/i386/headers.txt @@ -1,6 +1,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.alloca libc.include.assert + libc.include.pwd libc.include.cpio libc.include.langinfo ) diff --git a/libc/config/linux/riscv/headers.txt b/libc/config/linux/riscv/headers.txt index 199b0fa1e47c2..8b9b7c101a63f 100644 --- a/libc/config/linux/riscv/headers.txt +++ b/libc/config/linux/riscv/headers.txt @@ -31,6 +31,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.nl_types libc.include.poll libc.include.pthread + libc.include.pwd libc.include.sched libc.include.search libc.include.setjmp diff --git a/libc/config/linux/x86_64/headers.txt b/libc/config/linux/x86_64/headers.txt index 89090501c3a04..d36089c3da8ff 100644 --- a/libc/config/linux/x86_64/headers.txt +++ b/libc/config/linux/x86_64/headers.txt @@ -31,6 +31,7 @@ set(TARGET_PUBLIC_HEADERS libc.include.nl_types libc.include.poll libc.include.pthread + libc.include.pwd libc.include.sched libc.include.search libc.include.setjmp diff --git a/libc/hdr/types/CMakeLists.txt b/libc/hdr/types/CMakeLists.txt index 0c926050221f4..40833e0857478 100644 --- a/libc/hdr/types/CMakeLists.txt +++ b/libc/hdr/types/CMakeLists.txt @@ -153,6 +153,14 @@ add_proxy_header_library( libc.include.llvm-libc-types.struct_tm ) +add_proxy_header_library( + struct_passwd + HDRS + struct_passwd.h + FULL_BUILD_DEPENDS + libc.include.llvm-libc-types.struct_passwd +) + add_proxy_header_library( rsize_t HDRS diff --git a/libc/hdr/types/struct_passwd.h b/libc/hdr/types/struct_passwd.h new file mode 100644 index 0000000000000..cf82d6bca9b90 --- /dev/null +++ b/libc/hdr/types/struct_passwd.h @@ -0,0 +1,26 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Proxy for struct passwd. +/// +//===----------------------------------------------------------------------===// +#ifndef LLVM_LIBC_HDR_TYPES_STRUCT_PASSWD_H +#define LLVM_LIBC_HDR_TYPES_STRUCT_PASSWD_H + +#ifdef LIBC_FULL_BUILD + +#include "include/llvm-libc-types/struct_passwd.h" + +#else + +#include + +#endif // LIBC_FULL_BUILD + +#endif // LLVM_LIBC_HDR_TYPES_STRUCT_PASSWD_H diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt index 0f161f46af471..d51668bbdc1e5 100644 --- a/libc/include/CMakeLists.txt +++ b/libc/include/CMakeLists.txt @@ -490,6 +490,17 @@ add_header_macro( .llvm_libc_common_h ) +add_header_macro( + pwd + ../libc/include/pwd.yaml + pwd.h + DEPENDS + .llvm-libc-types.struct_passwd + .llvm-libc-types.uid_t + .llvm-libc-types.gid_t + .llvm_libc_common_h +) + if(LIBC_TARGET_ARCHITECTURE STREQUAL "x86_64") add_header_macro( ucontext diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt index 9dd7548aa5126..b6d7d14e81723 100644 --- a/libc/include/llvm-libc-types/CMakeLists.txt +++ b/libc/include/llvm-libc-types/CMakeLists.txt @@ -196,6 +196,7 @@ add_header( .blksize_t .blkcnt_t ) add_header(struct_tm HDR struct_tm.h) +add_header(struct_passwd HDR struct_passwd.h DEPENDS .uid_t .gid_t) add_header(struct_utsname HDR struct_utsname.h) add_header(thrd_start_t HDR thrd_start_t.h) add_header(thrd_t HDR thrd_t.h DEPENDS .__thread_type) diff --git a/libc/include/llvm-libc-types/struct_passwd.h b/libc/include/llvm-libc-types/struct_passwd.h new file mode 100644 index 0000000000000..c51d3349f7c87 --- /dev/null +++ b/libc/include/llvm-libc-types/struct_passwd.h @@ -0,0 +1,31 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Definition of struct passwd. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_TYPES_STRUCT_PASSWD_H +#define LLVM_LIBC_TYPES_STRUCT_PASSWD_H + +#include "gid_t.h" +#include "uid_t.h" + +/// Structure representing user account information from the password database. +struct passwd { + char *pw_name; // User's login name. + char *pw_passwd; // Encrypted password. + uid_t pw_uid; // Numerical user ID. + gid_t pw_gid; // Numerical group ID. + char *pw_gecos; // User info / real name. + char *pw_dir; // Initial working directory. + char *pw_shell; // Program to use as shell. +}; + +#endif // LLVM_LIBC_TYPES_STRUCT_PASSWD_H diff --git a/libc/include/pwd.yaml b/libc/include/pwd.yaml new file mode 100644 index 0000000000000..40db5690e98b9 --- /dev/null +++ b/libc/include/pwd.yaml @@ -0,0 +1,26 @@ +header: pwd.h +standards: + - posix +types: + - type_name: struct_passwd + - type_name: gid_t + - type_name: uid_t + - type_name: size_t +enums: [] +objects: [] +functions: + - name: getpwent + standards: + - posix + return_type: struct passwd * + arguments: [] + - name: setpwent + standards: + - posix + return_type: void + arguments: [] + - name: endpwent + standards: + - posix + return_type: void + arguments: [] diff --git a/libc/src/CMakeLists.txt b/libc/src/CMakeLists.txt index 2c7c4d00f150a..05944b582b5f8 100644 --- a/libc/src/CMakeLists.txt +++ b/libc/src/CMakeLists.txt @@ -56,3 +56,4 @@ add_subdirectory(signal) add_subdirectory(spawn) add_subdirectory(threads) add_subdirectory(ucontext) +add_subdirectory(pwd) diff --git a/libc/src/pwd/CMakeLists.txt b/libc/src/pwd/CMakeLists.txt new file mode 100644 index 0000000000000..f8498b27eaa54 --- /dev/null +++ b/libc/src/pwd/CMakeLists.txt @@ -0,0 +1,15 @@ +add_object_library( + pwd_utils + HDRS + pwd_utils.h + SRCS + pwd_utils.cpp + DEPENDS + libc.hdr.errno_macros + libc.hdr.types.struct_passwd + libc.src.string.string_utils + libc.src.__support.ctype_utils + libc.src.__support.error_or + libc.src.__support.str_to_integer + libc.src.__support.macros.config +) diff --git a/libc/src/pwd/pwd_utils.cpp b/libc/src/pwd/pwd_utils.cpp new file mode 100644 index 0000000000000..f56e6118328e8 --- /dev/null +++ b/libc/src/pwd/pwd_utils.cpp @@ -0,0 +1,73 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Helper functions for pwd. +/// +//===----------------------------------------------------------------------===// + +#include "src/pwd/pwd_utils.h" +#include "hdr/errno_macros.h" +#include "hdr/types/struct_passwd.h" +#include "src/__support/ctype_utils.h" +#include "src/__support/str_to_integer.h" +#include "src/string/string_utils.h" + +namespace LIBC_NAMESPACE_DECL { +namespace internal { + +ErrorOr parse_passwd_line(char *line) { + if (!line) + return Error(EINVAL); + + struct passwd pwd; + char *context = line; + + pwd.pw_name = string_token(nullptr, ":", &context); + if (!pwd.pw_name) + return Error(EINVAL); + + pwd.pw_passwd = string_token(nullptr, ":", &context); + if (!pwd.pw_passwd) + return Error(EINVAL); + + char *uid_str = string_token(nullptr, ":", &context); + if (!uid_str || !isdigit(uid_str[0])) + return Error(EINVAL); + auto uid_res = strtointeger(uid_str, 10); + if (uid_res.has_error() || uid_res.parsed_len == 0 || + uid_str[uid_res.parsed_len] != '\0') + return Error(EINVAL); + pwd.pw_uid = uid_res.value; + + char *gid_str = string_token(nullptr, ":", &context); + if (!gid_str || !isdigit(gid_str[0])) + return Error(EINVAL); + auto gid_res = strtointeger(gid_str, 10); + if (gid_res.has_error() || gid_res.parsed_len == 0 || + gid_str[gid_res.parsed_len] != '\0') + return Error(EINVAL); + pwd.pw_gid = gid_res.value; + + pwd.pw_gecos = string_token(nullptr, ":", &context); + if (!pwd.pw_gecos) + return Error(EINVAL); + + pwd.pw_dir = string_token(nullptr, ":", &context); + if (!pwd.pw_dir) + return Error(EINVAL); + + pwd.pw_shell = string_token(nullptr, ":", &context); + if (!pwd.pw_shell) + return Error(EINVAL); + + return pwd; +} + +} // namespace internal +} // namespace LIBC_NAMESPACE_DECL diff --git a/libc/src/pwd/pwd_utils.h b/libc/src/pwd/pwd_utils.h new file mode 100644 index 0000000000000..fe3b48179c54e --- /dev/null +++ b/libc/src/pwd/pwd_utils.h @@ -0,0 +1,30 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Declarations of helper functions for pwd. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIBC_SRC_PWD_PWD_UTILS_H +#define LLVM_LIBC_SRC_PWD_PWD_UTILS_H + +#include "hdr/types/struct_passwd.h" +#include "src/__support/error_or.h" +#include "src/__support/macros/config.h" + +namespace LIBC_NAMESPACE_DECL { +namespace internal { + +// Parses a colon-separated password database line into a struct passwd. +ErrorOr parse_passwd_line(char *line); + +} // namespace internal +} // namespace LIBC_NAMESPACE_DECL + +#endif // LLVM_LIBC_SRC_PWD_PWD_UTILS_H diff --git a/libc/test/src/CMakeLists.txt b/libc/test/src/CMakeLists.txt index 25a5c920036c3..01d6ee738135c 100644 --- a/libc/test/src/CMakeLists.txt +++ b/libc/test/src/CMakeLists.txt @@ -110,6 +110,7 @@ add_subdirectory(nl_types) add_subdirectory(signal) add_subdirectory(regex) add_subdirectory(spawn) +add_subdirectory(pwd) if(${LIBC_TARGET_OS} STREQUAL "linux") add_subdirectory(pthread) diff --git a/libc/test/src/pwd/CMakeLists.txt b/libc/test/src/pwd/CMakeLists.txt new file mode 100644 index 0000000000000..eb6c96f2f296f --- /dev/null +++ b/libc/test/src/pwd/CMakeLists.txt @@ -0,0 +1,13 @@ +add_custom_target(libc_pwd_unittests) + +add_libc_unittest( + pwd_utils_test + SUITE + libc_pwd_unittests + SRCS + pwd_utils_test.cpp + DEPENDS + libc.hdr.errno_macros + libc.hdr.types.struct_passwd + libc.src.pwd.pwd_utils +) diff --git a/libc/test/src/pwd/pwd_utils_test.cpp b/libc/test/src/pwd/pwd_utils_test.cpp new file mode 100644 index 0000000000000..528577f830e87 --- /dev/null +++ b/libc/test/src/pwd/pwd_utils_test.cpp @@ -0,0 +1,104 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Unittests for parse_passwd_line. +/// +//===----------------------------------------------------------------------===// + +#include "hdr/errno_macros.h" +#include "hdr/types/struct_passwd.h" +#include "src/pwd/pwd_utils.h" +#include "test/UnitTest/Test.h" + +TEST(LlvmLibcPwdTest, ParsePasswdLine_Success) { + char line[] = "root:x:0:0:root:/root:/bin/bash"; + auto res = LIBC_NAMESPACE::internal::parse_passwd_line(line); + ASSERT_TRUE(res.has_value()); + struct passwd pwd = res.value(); + ASSERT_STREQ(pwd.pw_name, "root"); + ASSERT_STREQ(pwd.pw_passwd, "x"); + ASSERT_EQ(pwd.pw_uid, 0u); + ASSERT_EQ(pwd.pw_gid, 0u); + ASSERT_STREQ(pwd.pw_gecos, "root"); + ASSERT_STREQ(pwd.pw_dir, "/root"); + ASSERT_STREQ(pwd.pw_shell, "/bin/bash"); +} + +TEST(LlvmLibcPwdTest, ParsePasswdLine_EmptyFields) { + char line[] = "root::0:0::/root:"; + auto res = LIBC_NAMESPACE::internal::parse_passwd_line(line); + ASSERT_TRUE(res.has_value()); + struct passwd pwd = res.value(); + ASSERT_STREQ(pwd.pw_name, "root"); + ASSERT_STREQ(pwd.pw_passwd, ""); + ASSERT_EQ(pwd.pw_uid, 0u); + ASSERT_EQ(pwd.pw_gid, 0u); + ASSERT_STREQ(pwd.pw_gecos, ""); + ASSERT_STREQ(pwd.pw_dir, "/root"); + ASSERT_STREQ(pwd.pw_shell, ""); +} + +TEST(LlvmLibcPwdTest, ParsePasswdLine_InvalidNumeric) { + char line1[] = "root:x:abc:0:root:/root:/bin/bash"; + auto res1 = LIBC_NAMESPACE::internal::parse_passwd_line(line1); + ASSERT_FALSE(res1.has_value()); + ASSERT_EQ(res1.error(), EINVAL); + + char line2[] = "root:x:0:def:root:/root:/bin/bash"; + auto res2 = LIBC_NAMESPACE::internal::parse_passwd_line(line2); + ASSERT_FALSE(res2.has_value()); + ASSERT_EQ(res2.error(), EINVAL); + + char line3[] = "root:x:-1:0:root:/root:/bin/bash"; + auto res3 = LIBC_NAMESPACE::internal::parse_passwd_line(line3); + ASSERT_FALSE(res3.has_value()); + ASSERT_EQ(res3.error(), EINVAL); + + char line4[] = "root:x:0:-1:root:/root:/bin/bash"; + auto res4 = LIBC_NAMESPACE::internal::parse_passwd_line(line4); + ASSERT_FALSE(res4.has_value()); + ASSERT_EQ(res4.error(), EINVAL); +} + +TEST(LlvmLibcPwdTest, ParsePasswdLine_MissingFields) { + char line[] = "root:x:0:0:root:/root"; + auto res = LIBC_NAMESPACE::internal::parse_passwd_line(line); + ASSERT_FALSE(res.has_value()); + ASSERT_EQ(res.error(), EINVAL); +} + +TEST(LlvmLibcPwdTest, ParsePasswdLine_NullInput) { + auto res = LIBC_NAMESPACE::internal::parse_passwd_line(nullptr); + ASSERT_FALSE(res.has_value()); + ASSERT_EQ(res.error(), EINVAL); +} + +TEST(LlvmLibcPwdTest, ParsePasswdLine_TrailingGarbage) { + char line1[] = "root:x:0a:0:root:/root:/bin/bash"; + auto res1 = LIBC_NAMESPACE::internal::parse_passwd_line(line1); + ASSERT_FALSE(res1.has_value()); + ASSERT_EQ(res1.error(), EINVAL); + + char line2[] = "root:x:0:0b:root:/root:/bin/bash"; + auto res2 = LIBC_NAMESPACE::internal::parse_passwd_line(line2); + ASSERT_FALSE(res2.has_value()); + ASSERT_EQ(res2.error(), EINVAL); +} + +TEST(LlvmLibcPwdTest, ParsePasswdLine_Overflow) { + char line1[] = "root:x:4294967296:0:root:/root:/bin/bash"; + auto res1 = LIBC_NAMESPACE::internal::parse_passwd_line(line1); + ASSERT_FALSE(res1.has_value()); + ASSERT_EQ(res1.error(), EINVAL); + + char line2[] = "root:x:0:4294967296:root:/root:/bin/bash"; + auto res2 = LIBC_NAMESPACE::internal::parse_passwd_line(line2); + ASSERT_FALSE(res2.has_value()); + ASSERT_EQ(res2.error(), EINVAL); +}