]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
test-nss-users: add new nss test that resolves users and groups
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Fri, 12 Mar 2021 13:25:56 +0000 (14:25 +0100)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Tue, 23 Mar 2021 13:14:08 +0000 (14:14 +0100)
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 ;)

src/basic/nss-util.h
src/test/meson.build
src/test/nss-test-util.c [new file with mode: 0644]
src/test/nss-test-util.h [new file with mode: 0644]
src/test/test-nss-hosts.c
src/test/test-nss-users.c [new file with mode: 0644]

index dfc0d3fb200e64bd4e06d59d1eeed4f09348d385..3c59dcc03c363b5106c4a8624c9b3d9aef65171b 100644 (file)
@@ -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);
index af55cbac82018e33aa687112a3495e2ee855c5ec..0488baba82090e61a798311003f5ad4621f72bb6 100644 (file)
@@ -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 (file)
index 0000000..fc1d724
--- /dev/null
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+
+#include <dlfcn.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#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 (file)
index 0000000..f081e64
--- /dev/null
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+#pragma once
+
+#include <nss.h>
+#include <stdint.h>
+
+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);
index 2e9414d16d54eea127f5baf5dd34a743bc3f65f7..1dd6aa083beb344476c2fc90a71e945127b0f9a7 100644 (file)
@@ -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"
 #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 (file)
index 0000000..c415c0c
--- /dev/null
@@ -0,0 +1,258 @@
+/* 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);