From: Zbigniew Jędrzejewski-Szmek Date: Fri, 12 Mar 2021 13:25:56 +0000 (+0100) Subject: test-nss-users: add new nss test that resolves users and groups X-Git-Tag: v249-rc1~508^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f0d1266821771724b09a00764f4e20478c77f0a2;p=thirdparty%2Fsystemd.git test-nss-users: add new nss test that resolves users and groups Inspired by https://bugzilla.redhat.com/show_bug.cgi?id=1929936. This is similar to test-nss-hosts, but does users, groups, uid, gids. Functions tested are: _nss_*_getpwnam_r _nss_*_getgrnam_r _nss_*_getpwgid_r _nss_*_getgrgid_r Other entry points should be tested too, but it's not relevant to the bug I was investigating, so I'm leaving that for later ;) --- diff --git a/src/basic/nss-util.h b/src/basic/nss-util.h index dfc0d3fb200..3c59dcc03c3 100644 --- a/src/basic/nss-util.h +++ b/src/basic/nss-util.h @@ -213,3 +213,25 @@ typedef enum nss_status (*_nss_gethostbyaddr_r_t)( struct hostent *host, char *buffer, size_t buflen, int *errnop, int *h_errnop); + +typedef enum nss_status (*_nss_getpwnam_r_t)( + const char *name, + struct passwd *pwd, + char *buffer, size_t buflen, + int *errnop); +typedef enum nss_status (*_nss_getpwuid_r_t)( + uid_t uid, + struct passwd *pwd, + char *buffer, size_t buflen, + int *errnop); + +typedef enum nss_status (*_nss_getgrnam_r_t)( + const char *name, + struct group *gr, + char *buffer, size_t buflen, + int *errnop); +typedef enum nss_status (*_nss_getgrgid_r_t)( + gid_t gid, + struct group *gr, + char *buffer, size_t buflen, + int *errnop); diff --git a/src/test/meson.build b/src/test/meson.build index af55cbac820..0488baba820 100644 --- a/src/test/meson.build +++ b/src/test/meson.build @@ -540,7 +540,16 @@ tests += [ [['src/test/test-gcrypt-util.c'], [], [], [], 'HAVE_GCRYPT'], - [['src/test/test-nss-hosts.c'], + [['src/test/test-nss-hosts.c', + 'src/test/nss-test-util.c', + 'src/test/nss-test-util.h'], + [], + [libdl], + [], 'ENABLE_NSS', 'manual'], + + [['src/test/test-nss-users.c', + 'src/test/nss-test-util.c', + 'src/test/nss-test-util.h'], [], [libdl], [], 'ENABLE_NSS', 'manual'], diff --git a/src/test/nss-test-util.c b/src/test/nss-test-util.c new file mode 100644 index 00000000000..fc1d724a2f2 --- /dev/null +++ b/src/test/nss-test-util.c @@ -0,0 +1,42 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#include + +#include "nss-test-util.h" +#include "string-util.h" + +const char* nss_status_to_string(enum nss_status status, char *buf, size_t buf_len) { + switch (status) { + case NSS_STATUS_TRYAGAIN: + return "NSS_STATUS_TRYAGAIN"; + case NSS_STATUS_UNAVAIL: + return "NSS_STATUS_UNAVAIL"; + case NSS_STATUS_NOTFOUND: + return "NSS_STATUS_NOTFOUND"; + case NSS_STATUS_SUCCESS: + return "NSS_STATUS_SUCCESS"; + case NSS_STATUS_RETURN: + return "NSS_STATUS_RETURN"; + default: + snprintf(buf, buf_len, "%i", status); + return buf; + } +}; + +void* nss_open_handle(const char *dir, const char *module, int flags) { + const char *path = NULL; + void *handle; + + if (dir) + path = strjoina(dir, "/libnss_", module, ".so.2"); + if (!path || access(path, F_OK) < 0) + path = strjoina("libnss_", module, ".so.2"); + + log_debug("Using %s", path); + handle = dlopen(path, flags); + if (!handle) + log_error("Failed to load module %s: %s", module, dlerror()); + return handle; +} diff --git a/src/test/nss-test-util.h b/src/test/nss-test-util.h new file mode 100644 index 00000000000..f081e6467ed --- /dev/null +++ b/src/test/nss-test-util.h @@ -0,0 +1,8 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ +#pragma once + +#include +#include + +const char* nss_status_to_string(enum nss_status status, char *buf, size_t buf_len); +void* nss_open_handle(const char *dir, const char *module, int flags); diff --git a/src/test/test-nss-hosts.c b/src/test/test-nss-hosts.c index 2e9414d16d5..1dd6aa083be 100644 --- a/src/test/test-nss-hosts.c +++ b/src/test/test-nss-hosts.c @@ -15,6 +15,7 @@ #include "local-addresses.h" #include "log.h" #include "main-func.h" +#include "nss-test-util.h" #include "nss-util.h" #include "path-util.h" #include "stdio-util.h" @@ -22,24 +23,6 @@ #include "strv.h" #include "tests.h" -static const char* nss_status_to_string(enum nss_status status, char *buf, size_t buf_len) { - switch (status) { - case NSS_STATUS_TRYAGAIN: - return "NSS_STATUS_TRYAGAIN"; - case NSS_STATUS_UNAVAIL: - return "NSS_STATUS_UNAVAIL"; - case NSS_STATUS_NOTFOUND: - return "NSS_STATUS_NOTFOUND"; - case NSS_STATUS_SUCCESS: - return "NSS_STATUS_SUCCESS"; - case NSS_STATUS_RETURN: - return "NSS_STATUS_RETURN"; - default: - snprintf(buf, buf_len, "%i", status); - return buf; - } -}; - static const char* af_to_string(int family, char *buf, size_t buf_len) { const char *name; @@ -54,22 +37,6 @@ static const char* af_to_string(int family, char *buf, size_t buf_len) { return buf; } -static void* open_handle(const char *dir, const char *module, int flags) { - const char *path = NULL; - void *handle; - - if (dir) - path = strjoina(dir, "/libnss_", module, ".so.2"); - if (!path || access(path, F_OK) < 0) - path = strjoina("libnss_", module, ".so.2"); - - log_debug("Using %s", path); - handle = dlopen(path, flags); - if (!handle) - log_error("Failed to load module %s: %s", module, dlerror()); - return handle; -} - static int print_gaih_addrtuples(const struct gaih_addrtuple *tuples) { int n = 0; @@ -414,7 +381,7 @@ static int test_one_module(const char *dir, log_info("======== %s ========", module); - handle = open_handle(dir, module, RTLD_LAZY|RTLD_NODELETE); + handle = nss_open_handle(dir, module, RTLD_LAZY|RTLD_NODELETE); if (!handle) return -EINVAL; @@ -437,10 +404,10 @@ static int parse_argv(int argc, char **argv, char ***the_names, struct local_address **the_addresses, int *n_addresses) { - int r, n = 0; _cleanup_strv_free_ char **modules = NULL, **names = NULL; _cleanup_free_ struct local_address *addrs = NULL; size_t n_allocated = 0; + int r, n = 0; if (argc > 1) modules = strv_new(argv[1]); @@ -495,12 +462,10 @@ static int parse_argv(int argc, char **argv, return n; } - *the_modules = modules; - *the_names = names; - modules = names = NULL; - *the_addresses = addrs; + *the_modules = TAKE_PTR(modules); + *the_names = TAKE_PTR(names); + *the_addresses = TAKE_PTR(addrs); *n_addresses = n; - addrs = NULL; return 0; } diff --git a/src/test/test-nss-users.c b/src/test/test-nss-users.c new file mode 100644 index 00000000000..c415c0ca3b7 --- /dev/null +++ b/src/test/test-nss-users.c @@ -0,0 +1,258 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include +#include +#include +#include + +#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);