1
0
mirror of https://sourceware.org/git/glibc.git synced 2025-07-29 11:41:21 +03:00

nsswitch: user new internal API (tests)

Testsuite support and new test for new API.

Reviewed-by: Siddhesh Poyarekar <siddhesh@sourceware.org>
This commit is contained in:
DJ Delorie
2020-11-09 22:08:04 -05:00
parent f8847d83e1
commit 6eceded941
7 changed files with 524 additions and 1 deletions

View File

@ -33,10 +33,12 @@
#include <pwd.h> #include <pwd.h>
#include <grp.h> #include <grp.h>
#include <netdb.h>
typedef struct test_tables { typedef struct test_tables {
struct passwd *pwd_table; struct passwd *pwd_table;
struct group *grp_table; struct group *grp_table;
struct hostent *host_table;
} test_tables; } test_tables;
extern void _nss_test1_init_hook (test_tables *) __attribute__((weak)); extern void _nss_test1_init_hook (test_tables *) __attribute__((weak));
@ -44,9 +46,11 @@ extern void _nss_test2_init_hook (test_tables *) __attribute__((weak));
#define PWD_LAST() { .pw_name = NULL, .pw_uid = 0 } #define PWD_LAST() { .pw_name = NULL, .pw_uid = 0 }
#define GRP_LAST() { .gr_name = NULL, .gr_gid = 0 } #define GRP_LAST() { .gr_name = NULL, .gr_gid = 0 }
#define HOST_LAST() { .h_name = NULL, .h_aliases = NULL, .h_length = 0, .h_addr_list = NULL }
#define PWD_ISLAST(p) ((p)->pw_name == NULL && (p)->pw_uid == 0) #define PWD_ISLAST(p) ((p)->pw_name == NULL && (p)->pw_uid == 0)
#define GRP_ISLAST(g) ((g)->gr_name == NULL && (g)->gr_gid == 0) #define GRP_ISLAST(g) ((g)->gr_name == NULL && (g)->gr_gid == 0)
#define HOST_ISLAST(h) ((h)->h_name == NULL && (h)->h_length == 0)
/* Macros to fill in the tables easily. */ /* Macros to fill in the tables easily. */
@ -72,6 +76,11 @@ extern void _nss_test2_init_hook (test_tables *) __attribute__((weak));
{ .gr_name = (char *) n, .gr_passwd = (char *) "*", .gr_gid = u, \ { .gr_name = (char *) n, .gr_passwd = (char *) "*", .gr_gid = u, \
.gr_mem = (char **) m } .gr_mem = (char **) m }
#define HOST(u) \
{ .h_name = (char *) "name" #u, .h_aliases = NULL, .h_addrtype = u, \
.h_length = 4, \
.h_addr_list = (char **) hostaddr_##u }
/*------------------------------------------------------------*/ /*------------------------------------------------------------*/
/* Helper functions for testing passwd entries. Call /* Helper functions for testing passwd entries. Call

View File

@ -66,6 +66,9 @@ static int npwd_data = default_npwd_data;
static struct group *grp_data = NULL; static struct group *grp_data = NULL;
static int ngrp_data = 0; static int ngrp_data = 0;
static struct hostent *host_data = NULL;
static int nhost_data = 0;
/* This function will get called, and once per session, look back into /* This function will get called, and once per session, look back into
the test case's executable for an init hook function, and call the test case's executable for an init hook function, and call
it. */ it. */
@ -99,6 +102,13 @@ init(void)
; ;
ngrp_data = i; ngrp_data = i;
} }
if (t.host_table)
{
host_data = t.host_table;
for (i=0; ! HOST_ISLAST(& host_data[i]); i++)
;
nhost_data = i;
}
} }
initted = 1; initted = 1;
} }
@ -280,7 +290,7 @@ NAME(getgrent_r) (struct group *result, char *buffer, size_t buflen,
++grp_iter; ++grp_iter;
} }
pthread_mutex_unlock (&pwd_lock); pthread_mutex_unlock (&grp_lock);
return res; return res;
} }
@ -312,3 +322,157 @@ NAME(getgrnam_r) (const char *name, struct group *result, char *buffer,
return NSS_STATUS_NOTFOUND; return NSS_STATUS_NOTFOUND;
} }
/* -------------------------------------------------- */
/* Host handling. */
static size_t host_iter;
#define CURHOST host_data[host_iter]
static pthread_mutex_t host_lock = PTHREAD_MUTEX_INITIALIZER;
enum nss_status
NAME(sethostent) (int stayopen)
{
init();
host_iter = 0;
return NSS_STATUS_SUCCESS;
}
enum nss_status
NAME(endhostent) (void)
{
init();
return NSS_STATUS_SUCCESS;
}
static enum nss_status
copy_host (struct hostent *result, struct hostent *local,
char *buffer, size_t buflen, int *errnop)
{
struct alloc_buffer buf = alloc_buffer_create (buffer, buflen);
char **memlist;
int i, j;
if (local->h_addr_list)
{
i = 0;
while (local->h_addr_list[i])
++i;
memlist = alloc_buffer_alloc_array (&buf, char *, i + 1);
if (memlist) {
for (j = 0; j < i; ++j)
memlist[j] = alloc_buffer_maybe_copy_string (&buf, local->h_addr_list[j]);
memlist[j] = NULL;
}
result->h_addr_list = memlist;
}
else
{
result->h_addr_list = NULL;
}
result->h_aliases = NULL;
result->h_addrtype = AF_INET;
result->h_length = 4;
result->h_name = alloc_buffer_maybe_copy_string (&buf, local->h_name);
if (alloc_buffer_has_failed (&buf))
{
*errnop = ERANGE;
return NSS_STATUS_TRYAGAIN;
}
return NSS_STATUS_SUCCESS;
}
enum nss_status
NAME(gethostent_r) (struct hostent *ret, char *buffer, size_t buflen,
struct hostent **result, int *errnop)
{
int res = NSS_STATUS_SUCCESS;
init();
pthread_mutex_lock (&host_lock);
if (host_iter >= nhost_data)
{
res = NSS_STATUS_NOTFOUND;
*result = NULL;
}
else
{
res = copy_host (ret, &CURHOST, buffer, buflen, errnop);
*result = ret;
++host_iter;
}
pthread_mutex_unlock (&host_lock);
return res;
}
enum nss_status
NAME(gethostbyname3_r) (const char *name, int af, struct hostent *ret,
char *buffer, size_t buflen, int *errnop,
int *h_errnop, int32_t *ttlp, char **canonp)
{
init();
for (size_t idx = 0; idx < nhost_data; ++idx)
if (strcmp (host_data[idx].h_name, name) == 0)
return copy_host (ret, & host_data[idx], buffer, buflen, h_errnop);
return NSS_STATUS_NOTFOUND;
}
enum nss_status
NAME(gethostbyname_r) (const char *name, struct hostent *result,
char *buffer, size_t buflen,
int *errnop, int *h_errnop)
{
return NAME(gethostbyname3_r) (name, AF_INET, result, buffer, buflen,
errnop, h_errnop, NULL, NULL);
}
enum nss_status
NAME(gethostbyname2_r) (const char *name, int af, struct hostent *result,
char *buffer, size_t buflen,
int *errnop, int *h_errnop)
{
return NAME(gethostbyname3_r) (name, af, result, buffer, buflen,
errnop, h_errnop, NULL, NULL);
}
enum nss_status
NAME(gethostbyaddr2_r) (const void *addr, socklen_t len, int af,
struct hostent *result, char *buffer, size_t buflen,
int *errnop, int *h_errnop, int32_t *ttlp)
{
init();
/* Support this later. */
if (len != 4)
return NSS_STATUS_NOTFOUND;
for (size_t idx = 0; idx < nhost_data; ++idx)
if (memcmp (host_data[idx].h_addr, addr, len) == 0)
return copy_host (result, & host_data[idx], buffer, buflen, h_errnop);
return NSS_STATUS_NOTFOUND;
}
/* Note: only the first address is supported, intentionally. */
enum nss_status
NAME(gethostbyaddr_r) (const void *addr, socklen_t len, int af,
struct hostent *result, char *buffer, size_t buflen,
int *errnop, int *h_errnop)
{
return NAME(gethostbyaddr2_r) (addr, len, af, result, buffer, buflen,
errnop, h_errnop, NULL);
}

341
nss/tst-reload1.c Normal file
View File

@ -0,0 +1,341 @@
/* Test that nsswitch.conf reloading actually works.
Copyright (C) 2020 Free Software Foundation, Inc.
This file is part of the GNU C Library.
The GNU C Library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
The GNU C Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with the GNU C Library; if not, see
<https://www.gnu.org/licenses/>. */
#include <nss.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <pwd.h>
#include <support/support.h>
#include <support/check.h>
#include "nss_test.h"
/* Size of buffers used by *_r functions. */
#define TESTBUFLEN 4096
static struct passwd pwd_table_1[] = {
PWD (100),
PWD (30),
PWD (200),
PWD (60),
PWD (20000),
PWD_LAST ()
};
static const char *hostaddr_5[] =
{
"ABCD", "abcd", "1234", NULL
};
static const char *hostaddr_15[] =
{
"4321", "ghij", NULL
};
static const char *hostaddr_25[] =
{
"WXYZ", NULL
};
static struct hostent host_table_1[] = {
HOST (5),
HOST (15),
HOST (25),
HOST_LAST ()
};
void
_nss_test1_init_hook(test_tables *t)
{
t->pwd_table = pwd_table_1;
t->host_table = host_table_1;
}
/* The first of these must not appear in pwd_table_1. */
static struct passwd pwd_table_2[] = {
PWD (5),
PWD_N(200, "name30"),
PWD (16),
PWD_LAST ()
};
static const char *hostaddr_6[] =
{
"mnop", NULL
};
static const char *hostaddr_16[] =
{
"7890", "a1b2", NULL
};
static const char *hostaddr_26[] =
{
"qwer", "tyui", NULL
};
static struct hostent host_table_2[] = {
HOST (6),
HOST (16),
HOST (26),
HOST_LAST ()
};
void
_nss_test2_init_hook(test_tables *t)
{
t->pwd_table = pwd_table_2;
t->host_table = host_table_2;
}
static void
must_be_tests (struct passwd *pt, struct hostent *ht)
{
int i;
struct hostent *h;
struct passwd *p;
for (i = 0; !PWD_ISLAST (&pt[i]); ++i)
{
p = getpwuid (pt[i].pw_uid);
TEST_VERIFY (p != NULL);
if (p != NULL)
{
TEST_VERIFY (strcmp (p->pw_name, pt[i].pw_name) == 0);
}
}
setpwent ();
for (i = 0; !PWD_ISLAST (&pt[i]); ++i)
{
p = getpwent ();
TEST_VERIFY (p != NULL);
if (p != NULL)
{
TEST_VERIFY (strcmp (p->pw_name, pt[i].pw_name) == 0);
TEST_VERIFY (p->pw_uid == pt[i].pw_uid);
}
}
endpwent ();
for (i = 0; !HOST_ISLAST (&ht[i]); ++i)
{
h = gethostbyname (ht[i].h_name);
TEST_VERIFY (h != NULL);
if (h != NULL)
{
TEST_VERIFY (strcmp (h->h_name, ht[i].h_name) == 0);
TEST_VERIFY (h->h_addr_list[0] != NULL);
if (h->h_addr_list[0])
TEST_VERIFY (strcmp (h->h_addr_list[0], ht[i].h_addr_list[0]) == 0);
}
}
for (i = 0; !HOST_ISLAST (&ht[i]); ++i)
{
struct hostent r, *rp;
char buf[TESTBUFLEN];
int herrno, res;
res = gethostbyname2_r (ht[i].h_name, AF_INET,
&r, buf, TESTBUFLEN, &rp, &herrno);
TEST_VERIFY (res == 0);
if (res == 0)
{
TEST_VERIFY (strcmp (r.h_name, ht[i].h_name) == 0);
TEST_VERIFY (r.h_addr_list[0] != NULL);
if (r.h_addr_list[0])
TEST_VERIFY (strcmp (r.h_addr_list[0], ht[i].h_addr_list[0]) == 0);
}
}
for (i = 0; !HOST_ISLAST (&ht[i]); ++i)
{
h = gethostbyaddr (ht[i].h_addr, 4, AF_INET);
TEST_VERIFY (h != NULL);
if (h != NULL)
{
TEST_VERIFY (strcmp (h->h_name, ht[i].h_name) == 0);
TEST_VERIFY (h->h_addr_list[0] != NULL);
if (h->h_addr_list[0])
TEST_VERIFY (strcmp (h->h_addr_list[0], ht[i].h_addr_list[0]) == 0);
}
}
/* getaddrinfo */
for (i = 0; !HOST_ISLAST (&ht[i]); ++i)
{
struct addrinfo *ap;
struct addrinfo hint;
int res, j;
memset (&hint, 0, sizeof (hint));
hint.ai_family = AF_INET;
hint.ai_socktype = SOCK_STREAM;
hint.ai_protocol = 0;
hint.ai_flags = 0;
ap = NULL;
res = getaddrinfo (ht[i].h_name, NULL, &hint, &ap);
TEST_VERIFY (res == 0);
TEST_VERIFY (ap != NULL);
if (res == 0 && ap != NULL)
{
j = 0; /* which address in the list */
while (ap)
{
struct sockaddr_in *in = (struct sockaddr_in *)ap->ai_addr;
unsigned char *up = (unsigned char *)&in->sin_addr;
TEST_VERIFY (memcmp (up, ht[i].h_addr_list[j], 4) == 0);
ap = ap->ai_next;
++j;
}
}
}
/* getnameinfo */
for (i = 0; !HOST_ISLAST (&ht[i]); ++i)
{
struct sockaddr_in addr;
int res;
char host_buf[NI_MAXHOST];
memset (&addr, 0, sizeof (addr));
addr.sin_family = AF_INET;
addr.sin_port = 80;
memcpy (& addr.sin_addr, ht[i].h_addr_list[0], 4);
res = getnameinfo ((struct sockaddr *) &addr, sizeof(addr),
host_buf, sizeof(host_buf),
NULL, 0, NI_NOFQDN);
TEST_VERIFY (res == 0);
if (res == 0)
TEST_VERIFY (strcmp (ht[i].h_name, host_buf) == 0);
else
printf ("error %s\n", gai_strerror (res));
}
}
static void
must_be_1 (void)
{
struct passwd *p;
must_be_tests (pwd_table_1, host_table_1);
p = getpwnam("name5");
TEST_VERIFY (p == NULL);
}
static void
must_be_2 (void)
{
struct passwd *p;
must_be_tests (pwd_table_2, host_table_2);
p = getpwnam("name100");
TEST_VERIFY (p == NULL);
}
static void
xrename (const char *a, const char *b)
{
int i = rename (a, b);
if (i != 0)
FAIL_EXIT1 ("rename(%s,%s) failed: %s\n", a, b, strerror(errno));
}
/* If the actions change while in the midst of doing a series of
lookups, make sure they're consistent. */
static void
test_cross_switch_consistency (void)
{
int i;
struct passwd *p;
/* We start by initiating a set/get/end loop on conf1. */
setpwent ();
for (i = 0; !PWD_ISLAST (&pwd_table_1[i]); ++i)
{
p = getpwent ();
TEST_VERIFY (p != NULL);
if (p != NULL)
{
TEST_VERIFY (strcmp (p->pw_name, pwd_table_1[i].pw_name) == 0);
TEST_VERIFY (p->pw_uid == pwd_table_1[i].pw_uid);
}
/* After the first lookup, switch to conf2 and verify */
if (i == 0)
{
xrename ("/etc/nsswitch.conf", "/etc/nsswitch.conf1");
xrename ("/etc/nsswitch.conf2", "/etc/nsswitch.conf");
p = getpwnam (pwd_table_2[0].pw_name);
TEST_VERIFY (p->pw_uid == pwd_table_2[0].pw_uid);
}
/* But the original loop should still be on conf1. */
}
endpwent ();
/* Make sure the set/get/end loop sees conf2 now. */
setpwent ();
for (i = 0; !PWD_ISLAST (&pwd_table_2[i]); ++i)
{
p = getpwent ();
TEST_VERIFY (p != NULL);
if (p != NULL)
{
TEST_VERIFY (strcmp (p->pw_name, pwd_table_2[i].pw_name) == 0);
TEST_VERIFY (p->pw_uid == pwd_table_2[i].pw_uid);
}
}
endpwent ();
}
static int
do_test (void)
{
/* The test1 module was configured at program start. */
must_be_1 ();
xrename ("/etc/nsswitch.conf", "/etc/nsswitch.conf1");
xrename ("/etc/nsswitch.conf2", "/etc/nsswitch.conf");
must_be_2 ();
xrename ("/etc/nsswitch.conf", "/etc/nsswitch.conf2");
xrename ("/etc/nsswitch.conf1", "/etc/nsswitch.conf");
must_be_1 ();
test_cross_switch_consistency ();
return 0;
}
#include <support/test-driver.c>

View File

@ -0,0 +1,3 @@
passwd: test1
group: test1
hosts: test1

View File

@ -0,0 +1,3 @@
passwd: test2
group: test2
hosts: test2

View File

@ -0,0 +1 @@
http 80/tcp

View File

@ -0,0 +1,2 @@
cp $B/nss/libnss_test1.so $L/libnss_test1.so.2
cp $B/nss/libnss_test2.so $L/libnss_test2.so.2