1
0
mirror of https://github.com/systemd/systemd.git synced 2025-01-31 05:47:30 +03:00

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 ;)
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2021-03-12 14:25:56 +01:00
parent 3f9721fef0
commit f0d1266821
6 changed files with 346 additions and 42 deletions

View 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);

View 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'],

42
src/test/nss-test-util.c Normal file
View File

@ -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;
}

8
src/test/nss-test-util.h Normal file
View File

@ -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);

View 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"
@ -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;
}

258
src/test/test-nss-users.c Normal file
View File

@ -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);