| /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
| |
| #include <pwd.h> |
| #include <stdlib.h> |
| #include <sys/types.h> |
| #include <unistd.h> |
| |
| #include "alloc-util.h" |
| #include "dlfcn-util.h" |
| #include "errno-list.h" |
| #include "format-util.h" |
| #include "log.h" |
| #include "main-func.h" |
| #include "nss-test-util.h" |
| #include "nss-util.h" |
| #include "path-util.h" |
| #include "parse-util.h" |
| #include "stdio-util.h" |
| #include "string-util.h" |
| #include "strv.h" |
| #include "tests.h" |
| #include "user-util.h" |
| |
| static size_t arg_bufsize = 1024; |
| |
| static void print_struct_passwd(const struct passwd *pwd) { |
| log_info(" \"%s\" / "UID_FMT":"GID_FMT, |
| pwd->pw_name, pwd->pw_uid, pwd->pw_gid); |
| log_info(" passwd=\"%s\"", pwd->pw_passwd); |
| log_info(" gecos=\"%s\"", pwd->pw_gecos); |
| log_info(" dir=\"%s\"", pwd->pw_dir); |
| log_info(" shell=\"%s\"", pwd->pw_shell); |
| } |
| |
| static void print_struct_group(const struct group *gr) { |
| _cleanup_free_ char *members = NULL; |
| |
| log_info(" \"%s\" / "GID_FMT, |
| gr->gr_name, gr->gr_gid); |
| log_info(" passwd=\"%s\"", gr->gr_passwd); |
| |
| assert_se(members = strv_join(gr->gr_mem, ", ")); |
| // FIXME: use shell_maybe_quote(SHELL_ESCAPE_EMPTY) when it becomes available |
| log_info(" members=%s", members); |
| } |
| |
| static void test_getpwnam_r(void *handle, const char *module, const char *name) { |
| const char *fname; |
| _nss_getpwnam_r_t f; |
| char buffer[arg_bufsize]; |
| int errno1 = 999; /* nss-dns doesn't set those */ |
| enum nss_status status; |
| char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; |
| struct passwd pwd; |
| |
| fname = strjoina("_nss_", module, "_getpwnam_r"); |
| f = dlsym(handle, fname); |
| log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); |
| if (!f) { |
| log_info("%s not defined", fname); |
| return; |
| } |
| |
| status = f(name, &pwd, buffer, sizeof buffer, &errno1); |
| log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s", |
| fname, name, |
| nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", |
| errno1, errno_to_name(errno1) ?: "---"); |
| if (status == NSS_STATUS_SUCCESS) |
| print_struct_passwd(&pwd); |
| } |
| |
| static void test_getgrnam_r(void *handle, const char *module, const char *name) { |
| const char *fname; |
| _nss_getgrnam_r_t f; |
| char buffer[arg_bufsize]; |
| int errno1 = 999; /* nss-dns doesn't set those */ |
| enum nss_status status; |
| char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; |
| struct group gr; |
| |
| fname = strjoina("_nss_", module, "_getgrnam_r"); |
| f = dlsym(handle, fname); |
| log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); |
| if (!f) { |
| log_info("%s not defined", fname); |
| return; |
| } |
| |
| status = f(name, &gr, buffer, sizeof buffer, &errno1); |
| log_info("%s(\"%s\") → status=%s%-20serrno=%d/%s", |
| fname, name, |
| nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", |
| errno1, errno_to_name(errno1) ?: "---"); |
| if (status == NSS_STATUS_SUCCESS) |
| print_struct_group(&gr); |
| } |
| |
| static void test_getpwuid_r(void *handle, const char *module, uid_t uid) { |
| const char *fname; |
| _nss_getpwuid_r_t f; |
| char buffer[arg_bufsize]; |
| int errno1 = 999; /* nss-dns doesn't set those */ |
| enum nss_status status; |
| char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; |
| struct passwd pwd; |
| |
| fname = strjoina("_nss_", module, "_getpwuid_r"); |
| f = dlsym(handle, fname); |
| log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); |
| if (!f) { |
| log_info("%s not defined", fname); |
| return; |
| } |
| |
| status = f(uid, &pwd, buffer, sizeof buffer, &errno1); |
| log_info("%s("UID_FMT") → status=%s%-20serrno=%d/%s", |
| fname, uid, |
| nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", |
| errno1, errno_to_name(errno1) ?: "---"); |
| if (status == NSS_STATUS_SUCCESS) |
| print_struct_passwd(&pwd); |
| } |
| |
| static void test_getgrgid_r(void *handle, const char *module, gid_t gid) { |
| const char *fname; |
| _nss_getgrgid_r_t f; |
| char buffer[arg_bufsize]; |
| int errno1 = 999; /* nss-dns doesn't set those */ |
| enum nss_status status; |
| char pretty_status[DECIMAL_STR_MAX(enum nss_status)]; |
| struct group gr; |
| |
| fname = strjoina("_nss_", module, "_getgrgid_r"); |
| f = dlsym(handle, fname); |
| log_debug("dlsym(0x%p, %s) → 0x%p", handle, fname, f); |
| if (!f) { |
| log_info("%s not defined", fname); |
| return; |
| } |
| |
| status = f(gid, &gr, buffer, sizeof buffer, &errno1); |
| log_info("%s("GID_FMT") → status=%s%-20serrno=%d/%s", |
| fname, gid, |
| nss_status_to_string(status, pretty_status, sizeof pretty_status), "\n", |
| errno1, errno_to_name(errno1) ?: "---"); |
| if (status == NSS_STATUS_SUCCESS) |
| print_struct_group(&gr); |
| } |
| |
| static void test_byname(void *handle, const char *module, const char *name) { |
| test_getpwnam_r(handle, module, name); |
| test_getgrnam_r(handle, module, name); |
| puts(""); |
| } |
| |
| static void test_byuid(void *handle, const char *module, uid_t uid) { |
| test_getpwuid_r(handle, module, uid); |
| test_getgrgid_r(handle, module, uid); |
| puts(""); |
| } |
| |
| static int test_one_module(const char *dir, |
| const char *module, |
| char **names) { |
| |
| log_info("======== %s ========", module); |
| |
| _cleanup_(dlclosep) void *handle = nss_open_handle(dir, module, RTLD_LAZY|RTLD_NODELETE); |
| if (!handle) |
| return -EINVAL; |
| |
| char **name; |
| STRV_FOREACH(name, names) |
| test_byname(handle, module, *name); |
| |
| STRV_FOREACH(name, names) { |
| uid_t uid; |
| |
| assert_cc(sizeof(uid_t) == sizeof(uint32_t)); |
| /* We use safe_atou32 because we don't want to refuse invalid uids. */ |
| if (safe_atou32(*name, &uid) < 0) |
| continue; |
| |
| test_byuid(handle, module, uid); |
| } |
| |
| log_info(" "); |
| return 0; |
| } |
| |
| static int parse_argv(int argc, char **argv, |
| char ***the_modules, |
| char ***the_names) { |
| |
| _cleanup_strv_free_ char **modules = NULL, **names = NULL; |
| const char *p; |
| int r; |
| |
| p = getenv("SYSTEMD_TEST_NSS_BUFSIZE"); |
| if (p) { |
| r = safe_atozu(p, &arg_bufsize); |
| if (r < 0) |
| return log_error_errno(r, "Failed to parse $SYSTEMD_TEST_NSS_BUFSIZE"); |
| } |
| |
| if (argc > 1) |
| modules = strv_new(argv[1]); |
| else |
| modules = strv_new( |
| #if ENABLE_NSS_SYSTEMD |
| "systemd", |
| #endif |
| #if ENABLE_NSS_MYMACHINES |
| "mymachines", |
| #endif |
| "files"); |
| assert_se(modules); |
| |
| if (argc > 2) |
| names = strv_copy(strv_skip(argv, 2)); |
| else |
| names = strv_new("root", |
| NOBODY_USER_NAME, |
| "foo_no_such_user", |
| "0", |
| "65534"); |
| assert_se(names); |
| |
| *the_modules = TAKE_PTR(modules); |
| *the_names = TAKE_PTR(names); |
| return 0; |
| } |
| |
| static int run(int argc, char **argv) { |
| _cleanup_free_ char *dir = NULL; |
| _cleanup_strv_free_ char **modules = NULL, **names = NULL; |
| char **module; |
| int r; |
| |
| test_setup_logging(LOG_INFO); |
| |
| r = parse_argv(argc, argv, &modules, &names); |
| if (r < 0) |
| return log_error_errno(r, "Failed to parse arguments: %m"); |
| |
| assert_se(path_extract_directory(argv[0], &dir) >= 0); |
| |
| STRV_FOREACH(module, modules) { |
| r = test_one_module(dir, *module, names); |
| if (r < 0) |
| return r; |
| } |
| |
| return 0; |
| } |
| |
| DEFINE_MAIN_FUNCTION(run); |