[PATCH v2 07/23] nss: Add __nss_generic_copy and __nss_generic_dup functions
Florian Weimer
fweimer@redhat.com
Fri Mar 20 20:41:58 GMT 2026
So far for struct group and struct passwd only.
These functions will be used internally in the type-generic NSS
implementation, to preserve intermediate results and to produce the
final *_r result.
---
nss/Makefile | 11 +-
nss/nss-lookups.def | 28 +++++
nss/nss_generic.h | 67 +++++++++++
nss/nss_generic_copy.c | 120 ++++++++++++++++++
nss/nss_generic_dup.c | 125 +++++++++++++++++++
nss/nss_generic_nscd.c | 29 +++++
nss/tst-nss_generic_copy.c | 241 +++++++++++++++++++++++++++++++++++++
nss/tst-nss_generic_dup.c | 231 +++++++++++++++++++++++++++++++++++
8 files changed, 851 insertions(+), 1 deletion(-)
create mode 100644 nss/nss-lookups.def
create mode 100644 nss/nss_generic.h
create mode 100644 nss/nss_generic_copy.c
create mode 100644 nss/nss_generic_dup.c
create mode 100644 nss/nss_generic_nscd.c
create mode 100644 nss/tst-nss_generic_copy.c
create mode 100644 nss/tst-nss_generic_dup.c
diff --git a/nss/Makefile b/nss/Makefile
index 42d28cf40a..ec68954f3b 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -45,6 +45,9 @@ routines = \
nss_files_data \
nss_files_fopen \
nss_files_functions \
+ nss_generic_copy \
+ nss_generic_dup \
+ nss_generic_nscd \
nss_hash \
nss_module \
nss_parse_line_result \
@@ -302,10 +305,16 @@ makedb-modules = xmalloc hash-string
others-extras = $(makedb-modules)
extra-objs += $(makedb-modules:=.o)
-tests-static = tst-field
+tests-static = \
+ tst-field \
+ tst-nss_generic_copy \
+ tst-nss_generic_dup \
+ # tests-static
tests-internal := \
tst-field \
+ tst-nss_generic_copy \
+ tst-nss_generic_dup \
tst-rfc3484 \
tst-rfc3484-2 \
tst-rfc3484-3 \
diff --git a/nss/nss-lookups.def b/nss/nss-lookups.def
new file mode 100644
index 0000000000..80ec36d170
--- /dev/null
+++ b/nss/nss-lookups.def
@@ -0,0 +1,28 @@
+/* Lookup type definitions for NSS.
+ Copyright (C) 2026 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/>. */
+
+DEFINE_LOOKUP (getgrgid, group, "getgrgid_r", grpdb)
+DEFINE_LOOKUP (getgrnam, group, "getgrnam_r", grpdb)
+DEFINE_LOOKUP (getpwnam, passwd, "getpwnam_r", pwddb)
+DEFINE_LOOKUP (getpwuid, passwd, "getpwuid_r", pwddb)
+
+/*
+ Local Variables:
+ mode:C
+ End:
+ */
diff --git a/nss/nss_generic.h b/nss/nss_generic.h
new file mode 100644
index 0000000000..fcddf8bf54
--- /dev/null
+++ b/nss/nss_generic.h
@@ -0,0 +1,67 @@
+/* Type- and query-agnostic NSS functionality.
+ Copyright (C) 2026 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/>. */
+
+/* The functions declared in this file use void * instead of concrete
+ pointer types such as struct group and NSS service module function
+ pointers. They can be combined to implement different high-level,
+ type-safe functions. */
+
+
+#ifndef NSS_GENERIC_H
+#define NSS_GENERIC_H
+
+#include <nsswitch.h>
+#include <stdint.h>
+
+enum nss_lookup_type
+ {
+#define DEFINE_LOOKUP(name, database, function_1, nscd) nss_lookup_##name,
+#include "nss-lookups.def"
+#undef DEFINE_LOOKUP
+ };
+
+/* Define the nss_lookup_MAX constant. This is not part of enum
+ nss_lookup_type so that switch coverage warnings work. */
+enum
+ {
+#define DEFINE_LOOKUP(name, database, function_1, nscd) \
+ HIDDEN_nss_lookup_##name,
+#include "nss-lookups.def"
+#undef DEFINE_LOOKUP
+ nss_lookup_MAX
+ };
+
+/* Copy an NSS struct corresponding to LT into RESULT, potentially
+ using additional storage of LENGTH bytes at BUFFER. If BUFFER is
+ not large enough, return ERANGE and set errno to ERANGE. Otherwise
+ return zero. */
+int __nss_generic_copy (enum nss_lookup_type lt, const void *source,
+ void *result, char *buffer, size_t length)
+ attribute_hidden;
+
+/* Create a malloc-allocated copy of the NSS struct of type LT at
+ SOURCE and return a pointer to it. Return NULL on allocation
+ failure. */
+void *__nss_generic_dup (enum nss_lookup_type lt, const void *source)
+ attribute_hidden;
+
+/* This array contains a dbtype value (see <nscd/nscd-dbtype.h>) for
+ each lookup type, or -1 if the lookup has no corresponding database. */
+extern int8_t __nscd_database_for_lookup[nss_lookup_MAX] attribute_hidden;
+
+#endif /* NSS_GENERIC_H */
diff --git a/nss/nss_generic_copy.c b/nss/nss_generic_copy.c
new file mode 100644
index 0000000000..62d6d40390
--- /dev/null
+++ b/nss/nss_generic_copy.c
@@ -0,0 +1,120 @@
+/* Copy data referenced in an NSS struct into a fixed-size destination buffer.
+ Copyright (C) 2026 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_generic.h>
+
+#include <alloc_buffer.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+
+static char *
+safe_copy_string (struct alloc_buffer *buf, const char *source)
+{
+ if (source == NULL)
+ return NULL;
+ else
+ return alloc_buffer_copy_string (buf, source);
+}
+
+/* Copy *SOURCE into *RESULT, interpreted according to LT. Use *BUF
+ to store referenced data for the deep copy. May mark *BUF as
+ failed. */
+static void
+__nss_do_copy (enum nss_lookup_type lt, const void *source, void *result,
+ struct alloc_buffer *buf);
+
+/* Implementation of __nss_do_copy for struct group. Use the same
+ function signature to help with switch compilation in __nss_do_copy. */
+static void
+__nss_copy_grp (enum nss_lookup_type lt, const struct group *source,
+ struct group *result, struct alloc_buffer *buf)
+{
+ result->gr_gid = source->gr_gid;
+
+ if (source->gr_mem == NULL)
+ result->gr_mem = NULL;
+ else
+ {
+ size_t member_count = 0;
+ while (source->gr_mem[member_count] != NULL)
+ ++member_count;
+
+ /* Copy the array first, to minimize the alignment requirements. */
+ result->gr_mem = alloc_buffer_alloc_array (buf, char *,
+ member_count + 1);
+ if (result->gr_mem == NULL)
+ return;
+
+ for (size_t i = 0; i < member_count; ++i)
+ result->gr_mem[i]
+ = alloc_buffer_copy_string (buf, source->gr_mem[i]);
+
+ result->gr_mem[member_count] = NULL;
+ }
+
+ result->gr_name = safe_copy_string (buf, source->gr_name);
+ result->gr_passwd = safe_copy_string (buf, source->gr_passwd);
+}
+
+/* Implementation of __nss_do_copy for struct passwd. */
+static void
+__nss_copy_pwd (enum nss_lookup_type lt, const struct passwd *source,
+ struct passwd *result, struct alloc_buffer *buf)
+{
+ result->pw_name = safe_copy_string (buf, source->pw_name);
+ result->pw_passwd = safe_copy_string (buf, source->pw_passwd);
+ result->pw_uid = source->pw_uid;
+ result->pw_gid = source->pw_gid;
+ result->pw_gecos = safe_copy_string (buf, source->pw_gecos);
+ result->pw_dir = safe_copy_string (buf, source->pw_dir);
+ result->pw_shell = safe_copy_string (buf, source->pw_shell);
+}
+
+static void
+__nss_do_copy (enum nss_lookup_type lt, const void *source, void *result,
+ struct alloc_buffer *buf)
+{
+ switch (lt)
+ {
+ case nss_lookup_getgrgid:
+ case nss_lookup_getgrnam:
+ return __nss_copy_grp (lt, source, result, buf);
+ case nss_lookup_getpwnam:
+ case nss_lookup_getpwuid:
+ return __nss_copy_pwd (lt, source, result, buf);
+ }
+ __builtin_unreachable ();
+}
+
+int
+__nss_generic_copy (enum nss_lookup_type lt, const void *source,
+ void *result, char *buffer, size_t length)
+{
+ struct alloc_buffer buf = alloc_buffer_create (buffer, length);
+
+ __nss_do_copy (lt, source, result, &buf);
+
+ if (alloc_buffer_has_failed (&buf))
+ {
+ __set_errno (ERANGE);
+ return ERANGE;
+ }
+
+ return 0;
+}
diff --git a/nss/nss_generic_dup.c b/nss/nss_generic_dup.c
new file mode 100644
index 0000000000..42939f2db9
--- /dev/null
+++ b/nss/nss_generic_dup.c
@@ -0,0 +1,125 @@
+/* Duplicate NSS data with a single malloc allocation.
+ Copyright (C) 2026 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_generic.h>
+
+#include <assert.h>
+#include <alloc_buffer.h>
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <string.h>
+
+/* Return 0 if SOURCE is a null pointer, otherwise the length of the
+ string including the terminating null byte. */
+static size_t
+safe_strlen_null (const char *source)
+{
+ if (source == NULL)
+ return 0;
+ else
+ return strlen (source) + 1;
+}
+
+/* Interpret SOURCE as a pointer to an NSS struct type according to
+ LT. Return the size of the buffer space required by the data in
+ *SOURCE, and write the size of the struct type itself to
+ *STRUCT_SIZE. */
+static size_t
+__nss_buffer_size (enum nss_lookup_type lt, const void *source,
+ size_t *struct_size);
+
+/* Variant of __nss_buffer_size for struct group. */
+static size_t
+__nss_group_buffer_size (const struct group *source, size_t *struct_size)
+{
+ *struct_size = sizeof (*source);
+ size_t size = 0;
+ size += safe_strlen_null (source->gr_name);
+ size += safe_strlen_null (source->gr_passwd);
+
+ /* Assume that the array is allocated first, so that no
+ alignment is needed. */
+ if (source->gr_mem != NULL)
+ {
+ for (size_t i = 0; source->gr_mem[i] != NULL; ++i)
+ size += sizeof (char *) + strlen (source->gr_mem[i]) + 1;
+ size += sizeof (char *);
+ }
+
+ return size;
+}
+
+/* Variant of __nss_buffer_size for struct passwd. */
+static size_t
+__nss_passwd_buffer_size (const struct passwd *source, size_t *struct_size)
+{
+ *struct_size = sizeof (*source);
+ size_t size = 0;
+ size += safe_strlen_null (source->pw_name);
+ size += safe_strlen_null (source->pw_passwd);
+ size += safe_strlen_null (source->pw_gecos);
+ size += safe_strlen_null (source->pw_dir);
+ size += safe_strlen_null (source->pw_shell);
+ return size;
+}
+
+static size_t
+__nss_buffer_size (enum nss_lookup_type lt, const void *source,
+ size_t *struct_size)
+{
+ switch (lt)
+ {
+ case nss_lookup_getgrgid:
+ case nss_lookup_getgrnam:
+ return __nss_group_buffer_size (source, struct_size);
+ case nss_lookup_getpwnam:
+ case nss_lookup_getpwuid:
+ return __nss_passwd_buffer_size (source, struct_size);
+ }
+
+ __builtin_unreachable ();
+}
+
+void *
+__nss_generic_dup (enum nss_lookup_type lt, const void *source)
+{
+ void *result;
+ char *buf;
+ char *end;
+ {
+ size_t struct_size;
+ size_t size = __nss_buffer_size (lt, source, &struct_size);
+ size_t alloc_size = struct_size + size;
+ result = malloc (alloc_size);
+ if (result == NULL)
+ return NULL;
+ buf = (char *) result + struct_size;
+ end = (char *) result + alloc_size;
+ }
+
+ int ret __attribute__ ((unused))
+ = __nss_generic_copy (lt, source, result, buf, end - buf);
+ /* If this assert fails, *SOURCE was concurrently modified, pointers
+ in *source aliased and collectively covered more than the address
+ space (so that size computation overflowed), or malloc did not
+ return the expected alignment. */
+ assert (ret == 0);
+
+ return result;
+}
diff --git a/nss/nss_generic_nscd.c b/nss/nss_generic_nscd.c
new file mode 100644
index 0000000000..28fc8446ae
--- /dev/null
+++ b/nss/nss_generic_nscd.c
@@ -0,0 +1,29 @@
+/* Generic malloc-compatible version of nscd get functions.
+ Copyright (C) 2026 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_generic.h>
+
+#include <nscd/nscd-dbtype.h>
+
+int8_t __nscd_database_for_lookup[nss_lookup_MAX] =
+ {
+#define DEFINE_LOOKUP(name, dbname, function, nscddb) \
+ [nss_lookup_##name] = nscddb,
+#include <nss-lookups.def>
+#undef DEFINE_LOOKUP
+ };
diff --git a/nss/tst-nss_generic_copy.c b/nss/tst-nss_generic_copy.c
new file mode 100644
index 0000000000..fd01d03e6e
--- /dev/null
+++ b/nss/tst-nss_generic_copy.c
@@ -0,0 +1,241 @@
+/* Test program for the __nss_generic_copy function.
+ Copyright (C) 2026 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/>. */
+
+/* This test needs to be statically linked because it accesses the
+ hidden function __nss_generic_copy. */
+
+#include <nss_generic.h>
+
+#include <errno.h>
+#include <grp.h>
+#include <pwd.h>
+#include <string.h>
+#include <support/check.h>
+#include <support/check_nss.h>
+
+static void
+test_group (enum nss_lookup_type lt)
+{
+ char *members[4] = { (char *) "user1", (char *) "user2", (char *) "user3", };
+ struct group source =
+ {
+ .gr_name = (char *) "group-name",
+ .gr_passwd = (char *) "password",
+ .gr_gid = 1000,
+ .gr_mem = members
+ };
+
+ /* Test with sufficient buffer size. */
+ {
+ char buffer[1024];
+ struct group result;
+
+ errno = 23587;
+ TEST_COMPARE (__nss_generic_copy (lt, &source, &result,
+ buffer, sizeof (buffer)),
+ 0);
+ TEST_VERIFY (errno != 0);
+
+ check_group ("sufficient buffer", &result,
+ "name: group-name\n"
+ "passwd: password\n"
+ "gid: 1000\n"
+ "member: user1\n"
+ "member: user2\n"
+ "member: user3\n");
+ }
+
+ /* Test with insufficient buffer size. */
+ {
+ char buffer[10];
+ struct group result;
+
+ errno = 23587;
+ int ret = __nss_generic_copy (lt, &source,
+ &result, buffer, sizeof (buffer));
+ TEST_COMPARE (ret, ERANGE);
+ TEST_COMPARE (errno, ERANGE);
+ }
+
+ /* Test with NULL members. */
+ {
+ source.gr_name = NULL;
+ source.gr_passwd = NULL;
+ source.gr_mem = NULL;
+
+ char buffer[1024];
+ struct group result;
+ memset (&result, 0xcc, sizeof (result));
+
+ errno = 23587;
+ TEST_COMPARE (__nss_generic_copy (lt, &source, &result,
+ buffer, sizeof (buffer)),
+ 0);
+ TEST_VERIFY (errno != 0);
+
+ check_group ("NULL group members", &result,
+ "name: (null)\n"
+ "passwd: (null)\n"
+ "gid: 1000\n"
+ "gr_mem: (null)\n");
+ }
+
+ /* Test with empty strings. */
+ {
+ char empty[] = "";
+ char *list[] = { empty, NULL };
+ source.gr_name = (char *) "";
+ source.gr_passwd = (char *) "";
+ source.gr_mem = list;
+
+ char buffer[1024];
+ struct group result;
+
+ errno = 23587;
+ TEST_COMPARE (__nss_generic_copy (lt, &source, &result,
+ buffer, sizeof (buffer)),
+ 0);
+ TEST_VERIFY (errno != 0);
+
+ check_group ("empty group strings", &result,
+ "name: \n"
+ "passwd: \n"
+ "gid: 1000\n"
+ "member: \n");
+ }
+}
+
+static void
+test_passwd (enum nss_lookup_type lt)
+{
+ struct passwd source_template =
+ {
+ .pw_name = (char *) "user-name",
+ .pw_passwd = (char *) "password",
+ .pw_uid = 2000,
+ .pw_gid = 3000,
+ .pw_gecos = (char *) "User Gecos",
+ .pw_dir = (char *) "/home/user",
+ .pw_shell = (char *) "/bin/sh",
+ };
+
+ /* Test with sufficient buffer size. */
+ {
+ struct passwd source = source_template;
+ char buffer[1024];
+ struct passwd result;
+
+ errno = 23587;
+ TEST_COMPARE (__nss_generic_copy (lt, &source, &result,
+ buffer, sizeof (buffer)),
+ 0);
+ TEST_VERIFY (errno != 0);
+
+ check_passwd ("sufficient buffer", &result,
+ "name: user-name\n"
+ "passwd: password\n"
+ "uid: 2000\n"
+ "gid: 3000\n"
+ "gecos: User Gecos\n"
+ "dir: /home/user\n"
+ "shell: /bin/sh\n");
+ }
+
+ /* Test with insufficient buffer size. */
+ {
+ struct passwd source = source_template;
+ char buffer[10];
+ struct passwd result;
+
+ errno = 23587;
+ int ret = __nss_generic_copy (lt, &source, &result,
+ buffer, sizeof (buffer));
+ TEST_COMPARE (ret, ERANGE);
+ TEST_COMPARE (errno, ERANGE);
+ }
+
+ /* Test with NULL strings. */
+ {
+ struct passwd source = source_template;
+ source.pw_name = NULL;
+ source.pw_passwd = NULL;
+ source.pw_gecos = NULL;
+ source.pw_dir = NULL;
+ source.pw_shell = NULL;
+
+ char buffer[1024];
+ struct passwd result;
+ memset (&result, 0xcc, sizeof (result));
+
+ errno = 23587;
+ TEST_COMPARE (__nss_generic_copy (lt, &source, &result,
+ buffer, sizeof (buffer)),
+ 0);
+ TEST_VERIFY (errno != 0);
+
+ check_passwd ("NULL passwd fields", &result,
+ "name: (null)\n"
+ "passwd: (null)\n"
+ "uid: 2000\n"
+ "gid: 3000\n"
+ "gecos: (null)\n"
+ "dir: (null)\n"
+ "shell: (null)\n");
+ }
+
+ /* Test with empty strings. */
+ {
+ struct passwd source = source_template;
+ source.pw_name = (char *) "";
+ source.pw_passwd = (char *) "";
+ source.pw_gecos = (char *) "";
+ source.pw_dir = (char *) "";
+ source.pw_shell = (char *) "";
+
+ char buffer[1024];
+ struct passwd result;
+
+ errno = 23587;
+ TEST_COMPARE (__nss_generic_copy (lt, &source, &result,
+ buffer, sizeof (buffer)),
+ 0);
+ TEST_VERIFY (errno != 0);
+
+ check_passwd ("empty passwd strings", &result,
+ "name: \n"
+ "passwd: \n"
+ "uid: 2000\n"
+ "gid: 3000\n"
+ "gecos: \n"
+ "dir: \n"
+ "shell: \n");
+ }
+}
+
+static int
+do_test (void)
+{
+ test_group (nss_lookup_getgrgid);
+ test_group (nss_lookup_getgrnam);
+ test_passwd (nss_lookup_getpwnam);
+ test_passwd (nss_lookup_getpwuid);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/nss/tst-nss_generic_dup.c b/nss/tst-nss_generic_dup.c
new file mode 100644
index 0000000000..4cf9557fc8
--- /dev/null
+++ b/nss/tst-nss_generic_dup.c
@@ -0,0 +1,231 @@
+/* Test program for the __nss_generic_dup function.
+ Copyright (C) 2026 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/>. */
+
+/* This test needs to be statically linked because it accesses the
+ hidden function __nss_generic_dup. */
+
+#include <nss_generic.h>
+
+#include <grp.h>
+#include <malloc.h>
+#include <pwd.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/check_nss.h>
+
+static void
+check_pointer_in_allocation (void *allocation, void *ptr)
+{
+ TEST_VERIFY (ptr >= allocation);
+ TEST_VERIFY (ptr < (void *) ((char *) allocation
+ + malloc_usable_size (allocation)));
+}
+
+static void
+test_group (enum nss_lookup_type lt)
+{
+ char *members[4] = { (char *) "user1", (char *) "user2",
+ (char *) "long-user-3-name" };
+ struct group source =
+ {
+ .gr_name = (char *) "group-name",
+ .gr_passwd = (char *) "password",
+ .gr_gid = 1000,
+ .gr_mem = members
+ };
+
+ /* Test with normal group data. */
+ {
+ struct group *result = __nss_generic_dup (lt, &source);
+ TEST_VERIFY (result != NULL);
+
+ check_group ("normal group data", result,
+ "name: group-name\n"
+ "passwd: password\n"
+ "gid: 1000\n"
+ "member: user1\n"
+ "member: user2\n"
+ "member: long-user-3-name\n");
+
+ check_pointer_in_allocation (result, result->gr_name);
+ check_pointer_in_allocation (result, result->gr_passwd);
+ check_pointer_in_allocation (result, result->gr_mem);
+ for (int i = 0; result->gr_mem[i] != NULL; i++)
+ check_pointer_in_allocation (result, result->gr_mem[i]);
+
+ free (result);
+ }
+
+ /* Test with NULL members. */
+ {
+ struct group source_null =
+ {
+ .gr_gid = 2000,
+ };
+
+ struct group *result = __nss_generic_dup (lt, &source_null);
+ TEST_VERIFY (result != NULL);
+
+ check_group ("NULL group members", result,
+ "name: (null)\n"
+ "passwd: (null)\n"
+ "gid: 2000\n"
+ "gr_mem: (null)\n");
+
+ free (result);
+ }
+
+ /* Test with empty strings. */
+ {
+ char empty[] = "";
+ char *empty_list[] = { empty, NULL };
+ struct group source_empty =
+ {
+ .gr_name = (char *) "",
+ .gr_passwd = (char *) "",
+ .gr_gid = 3000,
+ .gr_mem = empty_list
+ };
+
+ struct group *result = __nss_generic_dup (lt, &source_empty);
+ TEST_VERIFY (result != NULL);
+
+ check_group ("empty group strings", result,
+ "name: \n"
+ "passwd: \n"
+ "gid: 3000\n"
+ "member: \n");
+
+ check_pointer_in_allocation (result, result->gr_name);
+ check_pointer_in_allocation (result, result->gr_passwd);
+ check_pointer_in_allocation (result, result->gr_mem);
+ for (int i = 0; result->gr_mem[i] != NULL; i++)
+ check_pointer_in_allocation (result, result->gr_mem[i]);
+
+ free (result);
+ }
+}
+
+static void
+test_passwd (enum nss_lookup_type lt)
+{
+ struct passwd source =
+ {
+ .pw_name = (char *) "user-name",
+ .pw_passwd = (char *) "password",
+ .pw_uid = 1001,
+ .pw_gid = 1002,
+ .pw_gecos = (char *) "gecos-field",
+ .pw_dir = (char *) "/home/user",
+ .pw_shell = (char *) "/bin/sh"
+ };
+
+ /* Test with normal passwd data. */
+ {
+ struct passwd *result = __nss_generic_dup (lt, &source);
+ TEST_VERIFY (result != NULL);
+
+ check_passwd ("normal passwd data", result,
+ "name: user-name\n"
+ "passwd: password\n"
+ "uid: 1001\n"
+ "gid: 1002\n"
+ "gecos: gecos-field\n"
+ "dir: /home/user\n"
+ "shell: /bin/sh\n");
+
+ check_pointer_in_allocation (result, result->pw_name);
+ check_pointer_in_allocation (result, result->pw_passwd);
+ check_pointer_in_allocation (result, result->pw_gecos);
+ check_pointer_in_allocation (result, result->pw_dir);
+ check_pointer_in_allocation (result, result->pw_shell);
+
+ free (result);
+ }
+
+ /* Test with NULL fields. */
+ {
+ struct passwd source_null =
+ {
+ .pw_uid = 2001,
+ .pw_gid = 2002,
+ };
+
+ struct passwd *result = __nss_generic_dup (lt, &source_null);
+ TEST_VERIFY (result != NULL);
+
+ check_passwd ("NULL passwd fields", result,
+ "name: (null)\n"
+ "passwd: (null)\n"
+ "uid: 2001\n"
+ "gid: 2002\n"
+ "gecos: (null)\n"
+ "dir: (null)\n"
+ "shell: (null)\n");
+
+ free (result);
+ }
+
+ /* Test with empty strings. */
+ {
+ char empty[] = "";
+ struct passwd source_empty =
+ {
+ .pw_name = empty,
+ .pw_passwd = empty,
+ .pw_uid = 3001,
+ .pw_gid = 3002,
+ .pw_gecos = empty,
+ .pw_dir = empty,
+ .pw_shell = empty
+ };
+
+ struct passwd *result = __nss_generic_dup (lt, &source_empty);
+ TEST_VERIFY (result != NULL);
+
+ check_passwd ("empty passwd strings", result,
+ "name: \n"
+ "passwd: \n"
+ "uid: 3001\n"
+ "gid: 3002\n"
+ "gecos: \n"
+ "dir: \n"
+ "shell: \n");
+
+ check_pointer_in_allocation (result, result->pw_name);
+ check_pointer_in_allocation (result, result->pw_passwd);
+ check_pointer_in_allocation (result, result->pw_gecos);
+ check_pointer_in_allocation (result, result->pw_dir);
+ check_pointer_in_allocation (result, result->pw_shell);
+
+ free (result);
+ }
+}
+
+static int
+do_test (void)
+{
+ test_group (nss_lookup_getgrgid);
+ test_group (nss_lookup_getgrnam);
+ test_passwd (nss_lookup_getpwnam);
+ test_passwd (nss_lookup_getpwuid);
+
+ return 0;
+}
+
+#include <support/test-driver.c>
--
2.53.0
More information about the Libc-alpha
mailing list