From 1d768559f42375eb3bbb3ad0de52a6b49535e40c Mon Sep 17 00:00:00 2001 From: chegJH Date: Thu, 7 Apr 2022 17:33:06 +0800 Subject: [PATCH 05/16] Adapt to bionic libc, parser for passwd and group object Signed-off-by: chegJH --- .../modules/image/image_rootfs_handler.c | 25 ++ src/utils/cutils/utils_pwgr.c | 317 ++++++++++++++++++ src/utils/cutils/utils_pwgr.h | 33 ++ test/cutils/CMakeLists.txt | 1 + test/cutils/utils_pwgr/CMakeLists.txt | 29 ++ test/cutils/utils_pwgr/group_sample | 8 + test/cutils/utils_pwgr/passwd_sample | 11 + test/cutils/utils_pwgr/utils_pwgr_ut.cc | 101 ++++++ 8 files changed, 525 insertions(+) create mode 100644 src/utils/cutils/utils_pwgr.c create mode 100644 src/utils/cutils/utils_pwgr.h create mode 100644 test/cutils/utils_pwgr/CMakeLists.txt create mode 100644 test/cutils/utils_pwgr/group_sample create mode 100644 test/cutils/utils_pwgr/passwd_sample create mode 100644 test/cutils/utils_pwgr/utils_pwgr_ut.cc diff --git a/src/daemon/modules/image/image_rootfs_handler.c b/src/daemon/modules/image/image_rootfs_handler.c index f7bc9bc9..960d52c7 100644 --- a/src/daemon/modules/image/image_rootfs_handler.c +++ b/src/daemon/modules/image/image_rootfs_handler.c @@ -33,6 +33,7 @@ #include "path.h" #include "utils_convert.h" #include "utils_file.h" +#include "utils_pwgr.h" #define MINUID 0 #define MAXUID (((1LL << 31) - 1)) @@ -88,7 +89,11 @@ static int proc_by_fpasswd(FILE *f_passwd, const char *user, defs_process_user * struct passwd *pwbufp = NULL; if (f_passwd != NULL) { +#ifdef __ANDROID__ + errval = util_getpwent_r(f_passwd, &pw, buf, sizeof(buf), &pwbufp); +#else errval = fgetpwent_r(f_passwd, &pw, buf, sizeof(buf), &pwbufp); +#endif while (errval == 0 && pwbufp != NULL) { userfound = b_user_found(user, pwbufp); @@ -102,7 +107,11 @@ static int proc_by_fpasswd(FILE *f_passwd, const char *user, defs_process_user * *matched_username = util_strdup_s(pwbufp->pw_name); break; } +#ifdef __ANDROID__ + errval = util_getpwent_r(f_passwd, &pw, buf, sizeof(buf), &pwbufp); +#else errval = fgetpwent_r(f_passwd, &pw, buf, sizeof(buf), &pwbufp); +#endif } } @@ -212,14 +221,22 @@ static int do_proc_by_froup(FILE *f_group, const char *group, defs_process_user return 0; } +#ifdef __ANDROID__ + errval = util_getgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp); +#else errval = fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp); +#endif while (errval == 0 && gbufp != NULL) { // Treat numeric group as valid GID if (group == NULL) { if (search_group_list(gbufp, matched_username, puser) != 0) { return -1; } +#ifdef __ANDROID__ + errval = util_getgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp); +#else errval = fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp); +#endif continue; } @@ -229,7 +246,11 @@ static int do_proc_by_froup(FILE *f_group, const char *group, defs_process_user puser->gid = gbufp->gr_gid; *groupcnt = 1; } +#ifdef __ANDROID__ + errval = util_getgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp); +#else errval = fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp); +#endif } return 0; @@ -363,7 +384,11 @@ static int get_additional_groups(char **additional_groups, size_t additional_gro struct group *gbufp = NULL; struct group *groups = NULL; +#ifdef __ANDROID__ + while (f_group != NULL && util_getgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp) == 0) { +#else while (f_group != NULL && fgetgrent_r(f_group, &grp, buf, sizeof(buf), &gbufp) == 0) { +#endif for (i = 0; i < additional_groups_len; i++) { if (!group_matched(additional_groups[i], gbufp)) { continue; diff --git a/src/utils/cutils/utils_pwgr.c b/src/utils/cutils/utils_pwgr.c new file mode 100644 index 00000000..f4588268 --- /dev/null +++ b/src/utils/cutils/utils_pwgr.c @@ -0,0 +1,317 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. + * iSulad licensed under the Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v2 for more details. + * Author: hejunjie + * Create: 2022-04-08 + * Description: Provide line parser for android + *******************************************************************************/ + +#define _GNU_SOURCE +#include "utils_pwgr.h" + +#include +#include +#include +#include + +#include "isula_libutils/log.h" +#include "utils_string.h" +#include "utils_convert.h" +#include "utils_file.h" +#include "utils.h" + +static int hold_int(const char delim, bool required, char **src, unsigned int *dst) +{ + long long res = 0; + char *walker = *src; + char *err_str = NULL; + + if (**src == '\0') { + ERROR("Empty subject on given entrie is not allowed."); + return -1; + } + + while (*walker != delim) { + if (*walker == '\0') { + break; + } + ++walker; + } + + if (*walker == **src) { + if (required) { // deafult 0 while required full content but integer part is missing + *dst = 0; + *src = walker + 1; + return 0; + } + ERROR("Integer part is missing."); + ++(*src); + return -1; + } + + res = strtoll(*src, &err_str, 0); + if (errno == ERANGE) { + ERROR("Parse int from string failed."); + return -1; + } + if (res < 0) { + ERROR("Gid uid shall not be negative."); + return -1; + } + + if (sizeof(void *) > 4 && res > UINT_MAX) { // make sure 64-bit platform behave same as 32-bit + res = UINT_MAX; + } + res = res & UINT_MAX; + *dst = (uint32_t)res; + *src = err_str + 1; // update src to next valid context in line. + + return 0; +} + +static int hold_string(const char delim, char **src, char **dst) +{ + if (**src == delim) { // if src point to deliminator, content parsing is skiped. + *dst = ""; + *src = *src + 1; + return 0; + } + + if (**src == '\0') { + return 0; + } + + for (*dst = *src; **src != delim; ++(*src)) { + if (**src == '\0') { + break; + } + } + if (**src == delim) { + **src = '\0'; + ++(*src); + } + + return 0; +} + +static int parse_line_pw(const char delim, char *line, struct passwd *result) +{ + int ret = 0; + bool required = false; + + ret = hold_string(delim, &line, &result->pw_name); + if (ret != 0) { + ERROR("Parse name error."); + return ret; + } + + required = (result->pw_name[0] == '+' || result->pw_name[0] == '-') ? true : false; + + ret = hold_string(delim, &line, &result->pw_passwd); + if (ret != 0) { + ERROR("Parse passwd error."); + return ret; + } + + ret = hold_int(delim, required, &line, &result->pw_uid); + if (ret != 0) { + // a legitimate line must have uid + ERROR("Parse uid error."); + return ret; + } + ret = hold_int(delim, required, &line, &result->pw_gid); + if (ret != 0) { + // it's ok to not provide gid + ERROR("Parse gid error."); + } + + ret = hold_string(delim, &line, &result->pw_gecos); + if (ret != 0) { + ERROR("Parse gecos error."); + return ret; + } + + ret = hold_string(delim, &line, &result->pw_dir); + if (ret != 0) { + ERROR("Parse dir error."); + return ret; + } + + ret = hold_string(delim, &line, &result->pw_shell); + if (ret != 0) { + ERROR("Parse shell error."); + return ret; + } + + return ret; +} + +static char **hold_string_list(char **line, char *buf_start, char *buf_end, const char terminator) +{ + char **result = NULL; + char **walker = NULL; + + if (**line == '\0') { + return 0; + } + // For ultimate space usage, the blank area from buffer which was allocated from stack is used + buf_start += __alignof__(char *) - 1; + // align the starting position of the buffer to use it as a 2d array + buf_start -= (buf_start - (char *)0) % __alignof__(char *); + // record the starting position for latter return + result = (char **)buf_start; + // set stop edge for the buffer + walker = result; + + for (; walker < (char **)buf_end; ++walker) { + (void)util_trim_space(*line); + if (hold_string(',', line, walker) != 0) { + ERROR("Parse string list error."); + return NULL; + } + + if ((char *)(walker + 2) > buf_end) { + return NULL; + } + + if (**line == '\0') { + return result; + } + } + + return result; +} + +static int parse_line_gr(const char delim, char *line, size_t buflen, struct group *result) +{ + int ret = 0; + bool rf = false; + char *freebuff = line + 1 + strlen(line); + char *buffend = line + buflen; + + ret = hold_string(delim, &line, &result->gr_name); + if (ret != 0) { + ERROR("Parse name error."); + return ret; + } + + ret = hold_string(delim, &line, &result->gr_passwd); + if (ret != 0) { + ERROR("Parse gecos error."); + return ret; + } + if (result->gr_name[0] == '+' || result->gr_name[0] == '-') { + rf = true; + } + + ret = hold_int(delim, rf, &line, &result->gr_gid); + if (ret != 0) { + ERROR("Parse gid error."); + return ret; + } + + result->gr_mem = hold_string_list(&line, freebuff, buffend, ','); + + return 0; +} + +int util_getpwent_r(FILE *stream, struct passwd *resbuf, char *buffer, size_t buflen, struct passwd **result) +{ + const char delim = ':'; + + if (stream == NULL || resbuf == NULL || buffer == NULL) { + ERROR("Password obj, params is NULL."); + return -1; + } + + if (buflen <= 1) { + ERROR("Inadiquate buffer length was given."); + return -1; + } + + if (*result != NULL) { + ERROR("Result shall point to null to start."); + return -1; + } + + __fsetlocking(stream, FSETLOCKING_BYCALLER); + buffer[buflen - 1] = '\0'; + + if (feof(stream)) { + *result = NULL; + return ENOENT; + } + + while (fgets(buffer, buflen, stream) != NULL) { + (void)util_trim_space(buffer); + if (buffer[0] == '\0' || buffer[0] == '#' || strlen(buffer) < 1) { + continue; + } + + if (parse_line_pw(delim, buffer, resbuf) == 0) { + break; + } + + if (buffer[buflen - 1] != '\0') { + *result = NULL; + return ERANGE; + } + } + *result = resbuf; + + return 0; +} + +int util_getgrent_r(FILE *stream, struct group *resbuf, char *buffer, size_t buflen, struct group **result) +{ + const char delim = ':'; + + if (stream == NULL || resbuf == NULL || buffer == NULL) { + ERROR("Group obj, params is NULL."); + return -1; + } + + if (buflen <= 1) { + ERROR("Inadiquate buffer length was given."); + return -1; + } + + if (*result != NULL) { + ERROR("Result shall point to null to start."); + return -1; + } + + __fsetlocking(stream, FSETLOCKING_BYCALLER); + buffer[buflen - 1] = '\0'; + + if (feof(stream)) { + *result = NULL; + return ENOENT; + } + + while (fgets(buffer, buflen, stream) != NULL) { + (void)util_trim_space(buffer); + if (buffer[0] == '\0' || buffer[0] == '#' || strlen(buffer) < 1) { + continue; + } + + if (parse_line_gr(delim, buffer, buflen, resbuf) == 0) { + break; + } + + if (buffer[buflen - 1] != '\0') { + *result = NULL; + return ERANGE; + } + } + *result = resbuf; + + return 0; +} \ No newline at end of file diff --git a/src/utils/cutils/utils_pwgr.h b/src/utils/cutils/utils_pwgr.h new file mode 100644 index 00000000..45e38059 --- /dev/null +++ b/src/utils/cutils/utils_pwgr.h @@ -0,0 +1,33 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. + * iSulad licensed under the Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v2 for more details. + * Author: hejunjie + * Create: 2022-04-08 + * Description: Provide line parser for android + *******************************************************************************/ +#ifndef UTILS_CUTILS_UTILS_PWGR_H +#define UTILS_CUTILS_UTILS_PWGR_H + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +int util_getpwent_r(FILE *stream, struct passwd *resbuf, char *buffer, size_t buflen, struct passwd **result); + +int util_getgrent_r(FILE *stream, struct group *resbuf, char *buffer, size_t buflen, struct group **result); + +#ifdef __cplusplus +} +#endif +#endif // UTILS_CUTILS_UTILS_PWGR_H diff --git a/test/cutils/CMakeLists.txt b/test/cutils/CMakeLists.txt index 826255cd..b549f844 100644 --- a/test/cutils/CMakeLists.txt +++ b/test/cutils/CMakeLists.txt @@ -4,3 +4,4 @@ add_subdirectory(utils_string) add_subdirectory(utils_convert) add_subdirectory(utils_array) add_subdirectory(utils_base64) +add_subdirectory(utils_pwgr) diff --git a/test/cutils/utils_pwgr/CMakeLists.txt b/test/cutils/utils_pwgr/CMakeLists.txt new file mode 100644 index 00000000..548718da --- /dev/null +++ b/test/cutils/utils_pwgr/CMakeLists.txt @@ -0,0 +1,29 @@ +project(iSulad_UT) + +SET(EXE utils_pwgr_ut) + +add_executable(${EXE} + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_string.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_array.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_file.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_convert.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_verify.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_regex.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/utils_pwgr.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/sha256/sha256.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/map/map.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/map/rb_tree.c + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/path.c + utils_pwgr_ut.cc) + +target_include_directories(${EXE} PUBLIC + ${GTEST_INCLUDE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../../include + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/common + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils/map + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/sha256 + ${CMAKE_CURRENT_SOURCE_DIR}/../../../src/utils/cutils + ) +target_link_libraries(${EXE} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${ISULA_LIBUTILS_LIBRARY} -lcrypto -lyajl -lz) +add_test(NAME ${EXE} COMMAND ${EXE} --gtest_output=xml:${EXE}-Results.xml) diff --git a/test/cutils/utils_pwgr/group_sample b/test/cutils/utils_pwgr/group_sample new file mode 100644 index 00000000..c73883cc --- /dev/null +++ b/test/cutils/utils_pwgr/group_sample @@ -0,0 +1,8 @@ +root:x:0: +#bin:x:1: + +-adm:x:4: ++adm:x:4: +adm:x:4:a,list,of,users +adm:x:4:are,split,by,comma +adm:x:4:root,john, boob,jason \ No newline at end of file diff --git a/test/cutils/utils_pwgr/passwd_sample b/test/cutils/utils_pwgr/passwd_sample new file mode 100644 index 00000000..d4f3250d --- /dev/null +++ b/test/cutils/utils_pwgr/passwd_sample @@ -0,0 +1,11 @@ +root:x:0:0:root:/root:/bin/bash +bin:x:1:1:bin:/bin:/sbin/nologin +bin:x:-1:1:bin:/bin:/sbin/nologin +uidonly:x:1::bin:/bin:/sbin/nologin +::::1:1:bin:/bin:/sbin/nologin + +#npt:*:66:77::/etc/ntp:/sbin/nologin +npt:*:66:77::/etc/ntp:/sbin/nologin +npt:*:66:77::/etc/ntp:/sbin/nologin:some:extra:context:added ++npt:*::::/etc/ntp:/sbin/nologin +-npt:*::::/etc/ntp:/sbin/nologin \ No newline at end of file diff --git a/test/cutils/utils_pwgr/utils_pwgr_ut.cc b/test/cutils/utils_pwgr/utils_pwgr_ut.cc new file mode 100644 index 00000000..1a121f88 --- /dev/null +++ b/test/cutils/utils_pwgr/utils_pwgr_ut.cc @@ -0,0 +1,101 @@ +/****************************************************************************** + * Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. + * iSulad licensed under the Mulan PSL v2. + * You can use this software according to the terms and conditions of the Mulan PSL v2. + * You may obtain a copy of Mulan PSL v2 at: + * http://license.coscl.org.cn/MulanPSL2 + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR FIT FOR A PARTICULAR + * PURPOSE. + * See the Mulan PSL v2 for more details. + * Author: hejunjie + * Create: 2022-04-08 + * Description: utils_pwgr unit test + *******************************************************************************/ + +#include +#include "utils_pwgr.h" + +TEST(utils_pwgr, test_getpwent_r) +{ + std::string path = "../../../../test/cutils/utils_pwgr/passwd_sample"; + FILE *f_pw = fopen(path.c_str(), "r"); + ASSERT_NE(f_pw, nullptr); + + struct passwd pw; + struct passwd *ppw = nullptr; + char buf[BUFSIZ]; + + std::vector> testcase = { + std::make_tuple("root", "x", 0, 0, "root", "/root", "/bin/bash"), + std::make_tuple("bin", "x", 1, 1, "bin", "/bin", "/sbin/nologin"), + std::make_tuple("uidonly", "x", 1, 0, "bin", "/bin", "/sbin/nologin"), + std::make_tuple("npt", "*", 66, 77, "", "/etc/ntp", "/sbin/nologin"), + std::make_tuple("npt", "*", 66, 77, "", "/etc/ntp", "/sbin/nologin"), + std::make_tuple("+npt", "*", 0, 0, "", "/etc/ntp", "/sbin/nologin"), + std::make_tuple("-npt", "*", 0, 0, "", "/etc/ntp", "/sbin/nologin") + }; + + for (const auto &elem : testcase) { + ASSERT_EQ(util_getpwent_r(f_pw, &pw, buf, sizeof(buf), &ppw), 0); + ASSERT_STREQ(pw.pw_name, std::get<0>(elem).c_str()); + ASSERT_STREQ(pw.pw_passwd, std::get<1>(elem).c_str()); + ASSERT_EQ(pw.pw_uid, std::get<2>(elem)); + ASSERT_EQ(pw.pw_gid, std::get<3>(elem)); + ASSERT_STREQ(pw.pw_gecos, std::get<4>(elem).c_str()); + ASSERT_STREQ(pw.pw_dir, std::get<5>(elem).c_str()); + ASSERT_STREQ(pw.pw_shell, std::get<6>(elem).c_str()); + EXPECT_TRUE(ppw == &pw); + ppw = nullptr; + pw = {0}; + } + + fclose(f_pw); +} + +TEST(utils_pwgr, test_getgrent_r) +{ + std::string path = "../../../../test/cutils/utils_pwgr/group_sample"; + FILE *f_gr = fopen(path.c_str(), "r"); + ASSERT_NE(f_gr, nullptr); + + struct group gr{0}; + struct group *pgr = nullptr; + char buf[BUFSIZ]; + size_t i = 0; + size_t j = 0; + std::vector> string_list{ + {}, {}, {}, + {"a", "list", "of", "users"}, + {"are", "split", "by", "comma"}, + {"root", "john", "boob", "jason"} + }; + + std::vector> testcase = { + std::make_tuple("root", "x", 0), + std::make_tuple("-adm", "x", 4), + std::make_tuple("+adm", "x", 4), + std::make_tuple("adm", "x", 4), + std::make_tuple("adm", "x", 4), + std::make_tuple("adm", "x", 4), + }; + + for (; i < string_list.size(); ++i) { + ASSERT_EQ(util_getgrent_r(f_gr, &gr, buf, sizeof(buf), &pgr), 0); + ASSERT_STREQ(gr.gr_name, std::get<0>(testcase[i]).c_str()); + ASSERT_STREQ(gr.gr_passwd, std::get<1>(testcase[i]).c_str()); + ASSERT_EQ(gr.gr_gid, std::get<2>(testcase[i])); + if (string_list[i].size()) { + for (j = 0; j < string_list[i].size(); ++j) { + EXPECT_TRUE(strcmp(gr.gr_mem[j], string_list[i][j].c_str()) == 0); + } + } else { + EXPECT_TRUE(gr.gr_mem == nullptr); + } + EXPECT_TRUE(pgr == &gr); + gr = {0}; + pgr = nullptr; + } + + fclose(f_gr); +} \ No newline at end of file -- 2.20.1