about summary refs log tree commit diff
path: root/nss
diff options
context:
space:
mode:
authorArjun Shankar <arjun@redhat.com>2023-10-02 14:55:16 +0200
committerArjun Shankar <arjun@redhat.com>2023-10-24 12:30:59 +0200
commit98b107e30848198e7128f80b38b406585f0317d6 (patch)
tree647f9ea8ec2d63136020411d6dcfef63cdfcf7ca /nss
parentb121fdc552f392cd86b21f159dd3e3b998de91a3 (diff)
downloadglibc-98b107e30848198e7128f80b38b406585f0317d6.tar.gz
glibc-98b107e30848198e7128f80b38b406585f0317d6.tar.xz
glibc-98b107e30848198e7128f80b38b406585f0317d6.zip
Remove 'gshadow' and merge into 'nss'
The majority of gshadow routines are entry points for nss functionality.
This commit removes the 'gshadow' subdirectory and moves all
functionality and tests to 'nss'.  References to gshadow/ are
accordingly changed.
Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
Diffstat (limited to 'nss')
-rw-r--r--nss/Makefile28
-rw-r--r--nss/Versions19
-rw-r--r--nss/fgetsgent.c90
-rw-r--r--nss/fgetsgent_r.c46
-rw-r--r--nss/getsgent.c31
-rw-r--r--nss/getsgent_r.c32
-rw-r--r--nss/getsgnam.c31
-rw-r--r--nss/getsgnam_r.c32
-rw-r--r--nss/gshadow.h132
-rw-r--r--nss/putsgent.c81
-rw-r--r--nss/sgetsgent.c77
-rw-r--r--nss/sgetsgent_r.c77
-rw-r--r--nss/tst-fgetsgent_r.c192
-rw-r--r--nss/tst-gshadow.c141
-rw-r--r--nss/tst-putsgent.c168
-rw-r--r--nss/tst-sgetsgent.c69
16 files changed, 1246 insertions, 0 deletions
diff --git a/nss/Makefile b/nss/Makefile
index baf7d9d0ab..5256b90f8b 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -24,6 +24,7 @@ include ../Makeconfig
 
 headers := \
   grp.h \
+  gshadow.h \
   nss.h \
   # headers
 
@@ -79,6 +80,29 @@ CFLAGS-initgroups.c += -fexceptions
 CFLAGS-putgrent.c += -fexceptions $(libio-mtsafe)
 endif
 
+# gshadow routines:
+routines += \
+  fgetsgent \
+  fgetsgent_r \
+  getsgent \
+  getsgent_r \
+  getsgnam \
+  getsgnam_r \
+  putsgent \
+  sgetsgent \
+  sgetsgent_r \
+  # routines
+
+ifeq ($(have-thread-library),yes)
+CFLAGS-getsgent_r.c += -fexceptions
+CFLAGS-getsgent.c += -fexceptions
+CFLAGS-fgetsgent.c += -fexceptions
+CFLAGS-fgetsgent_r.c += -fexceptions $(libio-mtsafe)
+CFLAGS-putsgent.c += -fexceptions $(libio-mtsafe)
+CFLAGS-getsgnam.c += -fexceptions
+CFLAGS-getsgnam_r.c += -fexceptions
+endif
+
 # These are the databases that go through nss dispatch.
 # Caution: if you add a database here, you must add its real name
 # in databases.def, too.
@@ -118,6 +142,8 @@ tests := \
   test-digits-dots \
   test-netdb \
   testgrp \
+  tst-fgetsgent_r \
+  tst-gshadow \
   tst-nss-getpwent \
   tst-nss-hash \
   tst-nss-test1 \
@@ -126,6 +152,8 @@ tests := \
   tst-nss-test5 \
   tst-nss-test_errno \
   tst-putgrent \
+  tst-putsgent \
+  tst-sgetsgent \
   # tests
 
 xtests = bug-erange
diff --git a/nss/Versions b/nss/Versions
index 5401829911..6204ac0af1 100644
--- a/nss/Versions
+++ b/nss/Versions
@@ -37,6 +37,25 @@ libc {
     # g*
     getgrouplist;
   }
+  GLIBC_2.10 {
+    # e*
+    endsgent;
+
+    # f*
+    fgetsgent; fgetsgent_r;
+
+    # g*
+    getsgent; getsgent_r; getsgnam; getsgnam_r;
+
+    # p*
+    putsgent;
+
+    # s*
+    setsgent;
+
+    # s*
+    sgetsgent; sgetsgent_r;
+  }
   GLIBC_2.27 {
   }
   GLIBC_PRIVATE {
diff --git a/nss/fgetsgent.c b/nss/fgetsgent.c
new file mode 100644
index 0000000000..dc26b24b47
--- /dev/null
+++ b/nss/fgetsgent.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 2009-2023 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 <errno.h>
+#include <libc-lock.h>
+#include <gshadow.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <set-freeres.h>
+
+
+/* A reasonable size for a buffer to start with.  */
+#define BUFLEN_SPWD 1024
+
+/* We need to protect the dynamic buffer handling.  */
+__libc_lock_define_initialized (static, lock);
+
+static char *buffer;
+
+/* Read one shadow entry from the given stream.  */
+struct sgrp *
+fgetsgent (FILE *stream)
+{
+  static size_t buffer_size;
+  static struct sgrp resbuf;
+  fpos_t pos;
+  struct sgrp *result;
+  int save;
+
+  if (fgetpos (stream, &pos) != 0)
+    return NULL;
+
+  /* Get lock.  */
+  __libc_lock_lock (lock);
+
+  /* Allocate buffer if not yet available.  */
+  if (buffer == NULL)
+    {
+      buffer_size = BUFLEN_SPWD;
+      buffer = malloc (buffer_size);
+    }
+
+  while (buffer != NULL
+	 && (__fgetsgent_r (stream, &resbuf, buffer, buffer_size, &result)
+	     == ERANGE))
+    {
+      char *new_buf;
+      buffer_size += BUFLEN_SPWD;
+      new_buf = realloc (buffer, buffer_size);
+      if (new_buf == NULL)
+	{
+	  /* We are out of memory.  Free the current buffer so that the
+	     process gets a chance for a normal termination.  */
+	  save = errno;
+	  free (buffer);
+	  __set_errno (save);
+	}
+      buffer = new_buf;
+
+      /* Reset the stream.  */
+      if (fsetpos (stream, &pos) != 0)
+	buffer = NULL;
+    }
+
+  if (buffer == NULL)
+    result = NULL;
+
+  /* Release lock.  Preserve error value.  */
+  save = errno;
+  __libc_lock_unlock (lock);
+  __set_errno (save);
+
+  return result;
+}
+
+weak_alias (buffer, __libc_fgetsgent_freeres_ptr);
diff --git a/nss/fgetsgent_r.c b/nss/fgetsgent_r.c
new file mode 100644
index 0000000000..54616b9bd3
--- /dev/null
+++ b/nss/fgetsgent_r.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 2009-2023 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 <ctype.h>
+#include <errno.h>
+#include <gshadow.h>
+#include <stdio.h>
+
+/* Define a line parsing function using the common code
+   used in the nss_files module.  */
+
+#define STRUCTURE	sgrp
+#define ENTNAME		sgent
+#define	EXTERN_PARSER	1
+struct sgent_data {};
+
+#include <nss/nss_files/files-parse.c>
+
+
+/* Read one shadow entry from the given stream.  */
+int
+__fgetsgent_r (FILE *stream, struct sgrp *resbuf, char *buffer, size_t buflen,
+	       struct sgrp **result)
+{
+  int ret = __nss_fgetent_r (stream, resbuf, buffer, buflen, parse_line);
+  if (ret == 0)
+    *result = resbuf;
+  else
+    *result = NULL;
+  return ret;
+}
+weak_alias (__fgetsgent_r, fgetsgent_r)
diff --git a/nss/getsgent.c b/nss/getsgent.c
new file mode 100644
index 0000000000..68f67c0940
--- /dev/null
+++ b/nss/getsgent.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 2009-2023 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 <gshadow.h>
+
+
+#define LOOKUP_TYPE	struct sgrp
+#define SETFUNC_NAME	setsgent
+#define	GETFUNC_NAME	getsgent
+#define	ENDFUNC_NAME	endsgent
+#define DATABASE_NAME	gshadow
+#define BUFLEN		1024
+
+/* There is no nscd support for the shadow file.  */
+#undef	USE_NSCD
+
+#include "../nss/getXXent.c"
diff --git a/nss/getsgent_r.c b/nss/getsgent_r.c
new file mode 100644
index 0000000000..b24ebe1796
--- /dev/null
+++ b/nss/getsgent_r.c
@@ -0,0 +1,32 @@
+/* Copyright (C) 2009-2023 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 <gshadow.h>
+
+
+#define LOOKUP_TYPE		struct sgrp
+#define SETFUNC_NAME		setsgent
+#define	GETFUNC_NAME		getsgent
+#define	ENDFUNC_NAME		endsgent
+#define DATABASE_NAME		gshadow
+#define BUFLEN			1024
+#define NO_COMPAT_NEEDED	1
+
+/* There is no nscd support for the shadow file.  */
+#undef	USE_NSCD
+
+#include "../nss/getXXent_r.c"
diff --git a/nss/getsgnam.c b/nss/getsgnam.c
new file mode 100644
index 0000000000..3177c864bd
--- /dev/null
+++ b/nss/getsgnam.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 2009-2023 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 <gshadow.h>
+
+
+#define LOOKUP_TYPE	struct sgrp
+#define FUNCTION_NAME	getsgnam
+#define DATABASE_NAME	gshadow
+#define ADD_PARAMS	const char *name
+#define ADD_VARIABLES	name
+#define BUFLEN		1024
+
+/* There is no nscd support for the shadow file.  */
+#undef	USE_NSCD
+
+#include "../nss/getXXbyYY.c"
diff --git a/nss/getsgnam_r.c b/nss/getsgnam_r.c
new file mode 100644
index 0000000000..a7bc0b0396
--- /dev/null
+++ b/nss/getsgnam_r.c
@@ -0,0 +1,32 @@
+/* Copyright (C) 2009-2023 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 <gshadow.h>
+
+
+#define LOOKUP_TYPE		struct sgrp
+#define FUNCTION_NAME		getsgnam
+#define DATABASE_NAME		gshadow
+#define ADD_PARAMS		const char *name
+#define ADD_VARIABLES		name
+#define BUFLEN			1024
+#define NO_COMPAT_NEEDED	1
+
+/* There is no nscd support for the shadow file.  */
+#undef	USE_NSCD
+
+#include "../nss/getXXbyYY_r.c"
diff --git a/nss/gshadow.h b/nss/gshadow.h
new file mode 100644
index 0000000000..078e145e46
--- /dev/null
+++ b/nss/gshadow.h
@@ -0,0 +1,132 @@
+/* Copyright (C) 2009-2023 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/>.  */
+
+/* Declaration of types and functions for shadow group suite.  */
+
+#ifndef _GSHADOW_H
+#define _GSHADOW_H	1
+
+#include <features.h>
+#include <paths.h>
+#include <bits/types/FILE.h>
+
+#define __need_size_t
+#include <stddef.h>
+
+/* Path to the user database files.  */
+#define	GSHADOW _PATH_GSHADOW
+
+
+__BEGIN_DECLS
+
+/* Structure of the group file.  */
+struct sgrp
+  {
+    char *sg_namp;		/* Group name.  */
+    char *sg_passwd;		/* Encrypted password.  */
+    char **sg_adm;		/* Group administrator list.  */
+    char **sg_mem;		/* Group member list.  */
+  };
+
+
+/* Open database for reading.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern void setsgent (void);
+
+/* Close database.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern void endsgent (void);
+
+/* Get next entry from database, perhaps after opening the file.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern struct sgrp *getsgent (void);
+
+/* Get shadow entry matching NAME.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern struct sgrp *getsgnam (const char *__name);
+
+/* Read shadow entry from STRING.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern struct sgrp *sgetsgent (const char *__string);
+
+/* Read next shadow entry from STREAM.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern struct sgrp *fgetsgent (FILE *__stream);
+
+/* Write line containing shadow password entry to stream.
+
+   This function is not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation it is a cancellation point and
+   therefore not marked with __THROW.  */
+extern int putsgent (const struct sgrp *__g, FILE *__stream);
+
+
+#ifdef __USE_MISC
+/* Reentrant versions of some of the functions above.
+
+   These functions are not part of POSIX and therefore no official
+   cancellation point.  But due to similarity with an POSIX interface
+   or due to the implementation they are cancellation points and
+   therefore not marked with __THROW.  */
+extern int getsgent_r (struct sgrp *__result_buf, char *__buffer,
+		       size_t __buflen, struct sgrp **__result)
+	__attr_access ((__write_only__, 2, 3));
+
+extern int getsgnam_r (const char *__name, struct sgrp *__result_buf,
+		       char *__buffer, size_t __buflen,
+		       struct sgrp **__result)
+	__attr_access ((__write_only__, 3, 4));
+
+extern int sgetsgent_r (const char *__string, struct sgrp *__result_buf,
+			char *__buffer, size_t __buflen,
+			struct sgrp **__result)
+	__attr_access ((__write_only__, 3, 4));
+
+extern int fgetsgent_r (FILE *__stream, struct sgrp *__result_buf,
+			char *__buffer, size_t __buflen,
+			struct sgrp **__result)
+	__attr_access ((__write_only__, 3, 4));
+#endif	/* misc */
+
+__END_DECLS
+
+#endif /* gshadow.h */
diff --git a/nss/putsgent.c b/nss/putsgent.c
new file mode 100644
index 0000000000..cd48eb2c72
--- /dev/null
+++ b/nss/putsgent.c
@@ -0,0 +1,81 @@
+/* Copyright (C) 2009-2023 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 <errno.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <gshadow.h>
+#include <nss.h>
+
+#define _S(x)	x ? x : ""
+
+
+/* Write an entry to the given stream.
+   This must know the format of the group file.  */
+int
+putsgent (const struct sgrp *g, FILE *stream)
+{
+  int errors = 0;
+
+  if (g->sg_namp == NULL || !__nss_valid_field (g->sg_namp)
+      || !__nss_valid_field (g->sg_passwd)
+      || !__nss_valid_list_field (g->sg_adm)
+      || !__nss_valid_list_field (g->sg_mem))
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+
+  _IO_flockfile (stream);
+
+  if (fprintf (stream, "%s:%s:", g->sg_namp, _S (g->sg_passwd)) < 0)
+    ++errors;
+
+  bool first = true;
+  char **sp = g->sg_adm;
+  if (sp != NULL)
+    while (*sp != NULL)
+      {
+	if (fprintf (stream, "%s%s", first ? "" : ",", *sp++) < 0)
+	  {
+	    ++errors;
+	    break;
+	  }
+	first = false;
+      }
+  if (putc_unlocked (':', stream) == EOF)
+    ++errors;
+
+  first = true;
+  sp = g->sg_mem;
+  if (sp != NULL)
+    while (*sp != NULL)
+      {
+	if (fprintf (stream, "%s%s", first ? "" : ",", *sp++) < 0)
+	  {
+	    ++errors;
+	    break;
+	  }
+	first = false;
+      }
+  if (putc_unlocked ('\n', stream) == EOF)
+    ++errors;
+
+  _IO_funlockfile (stream);
+
+  return errors ? -1 : 0;
+}
diff --git a/nss/sgetsgent.c b/nss/sgetsgent.c
new file mode 100644
index 0000000000..92302f7240
--- /dev/null
+++ b/nss/sgetsgent.c
@@ -0,0 +1,77 @@
+/* Copyright (C) 2009-2023 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 <errno.h>
+#include <libc-lock.h>
+#include <gshadow.h>
+#include <stdlib.h>
+
+
+/* A reasonable size for a buffer to start with.  */
+#define BUFLEN_SPWD 1024
+
+/* We need to protect the dynamic buffer handling.  */
+__libc_lock_define_initialized (static, lock);
+
+/* Read one shadow entry from the given stream.  */
+struct sgrp *
+sgetsgent (const char *string)
+{
+  static char *buffer;
+  static size_t buffer_size;
+  static struct sgrp resbuf;
+  struct sgrp *result;
+  int save;
+
+  /* Get lock.  */
+  __libc_lock_lock (lock);
+
+  /* Allocate buffer if not yet available.  */
+  if (buffer == NULL)
+    {
+      buffer_size = BUFLEN_SPWD;
+      buffer = malloc (buffer_size);
+    }
+
+  while (buffer != NULL
+	 && __sgetsgent_r (string, &resbuf, buffer, buffer_size, &result) != 0
+	 && errno == ERANGE)
+    {
+      char *new_buf;
+      buffer_size += BUFLEN_SPWD;
+      new_buf = realloc (buffer, buffer_size);
+      if (new_buf == NULL)
+	{
+	  /* We are out of memory.  Free the current buffer so that the
+	     process gets a chance for a normal termination.  */
+	  save = errno;
+	  free (buffer);
+	  __set_errno (save);
+	}
+      buffer = new_buf;
+    }
+
+  if (buffer == NULL)
+    result = NULL;
+
+  /* Release lock.  Preserve error value.  */
+  save = errno;
+  __libc_lock_unlock (lock);
+  __set_errno (save);
+
+  return result;
+}
diff --git a/nss/sgetsgent_r.c b/nss/sgetsgent_r.c
new file mode 100644
index 0000000000..c75624e1f7
--- /dev/null
+++ b/nss/sgetsgent_r.c
@@ -0,0 +1,77 @@
+/* Copyright (C) 2009-2023 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 <ctype.h>
+#include <errno.h>
+#include <gshadow.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Define a line parsing function using the common code
+   used in the nss_files module.  */
+
+#define STRUCTURE	sgrp
+#define ENTNAME		sgent
+struct sgent_data {};
+
+
+#define TRAILING_LIST_MEMBER		sg_mem
+#define TRAILING_LIST_SEPARATOR_P(c)	((c) == ',')
+#include <nss/nss_files/files-parse.c>
+LINE_PARSER
+(,
+ STRING_FIELD (result->sg_namp, ISCOLON, 0);
+ if (line[0] == '\0'
+     && (result->sg_namp[0] == '+' || result->sg_namp[0] == '-'))
+   {
+     result->sg_passwd = NULL;
+     result->sg_adm = NULL;
+     result->sg_mem = NULL;
+   }
+ else
+   {
+     STRING_FIELD (result->sg_passwd, ISCOLON, 0);
+     STRING_LIST (result->sg_adm, ':');
+   }
+ )
+
+
+/* Read one shadow entry from the given stream.  */
+int
+__sgetsgent_r (const char *string, struct sgrp *resbuf, char *buffer,
+	       size_t buflen, struct sgrp **result)
+{
+  char *sp;
+  if (string < buffer || string >= buffer + buflen)
+    {
+      buffer[buflen - 1] = '\0';
+      sp = strncpy (buffer, string, buflen);
+      if (buffer[buflen - 1] != '\0')
+	{
+	  __set_errno (ERANGE);
+	  return ERANGE;
+	}
+    }
+  else
+    sp = (char *) string;
+
+  int parse_result = parse_line (sp, resbuf, (void *) buffer, buflen, &errno);
+  *result = parse_result > 0 ? resbuf : NULL;
+
+  return *result == NULL ? errno : 0;
+}
+weak_alias (__sgetsgent_r, sgetsgent_r)
diff --git a/nss/tst-fgetsgent_r.c b/nss/tst-fgetsgent_r.c
new file mode 100644
index 0000000000..17558fa28d
--- /dev/null
+++ b/nss/tst-fgetsgent_r.c
@@ -0,0 +1,192 @@
+/* Test for fgetsgent_r and buffer sizes.
+   Copyright (C) 2020-2023 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 <array_length.h>
+#include <errno.h>
+#include <gshadow.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/temp_file.h>
+#include <support/xmemstream.h>
+#include <support/xstdio.h>
+
+/* Turn a parsed struct back into a line string.  The returned string
+   should be freed.  */
+static char *
+format_ent (const struct sgrp *e)
+{
+  struct xmemstream stream;
+  xopen_memstream (&stream);
+  TEST_COMPARE (putsgent (e, stream.out), 0);
+  xfclose_memstream (&stream);
+  return stream.buffer;
+}
+
+/* An entry in the input file along with the expected output.  */
+struct input
+{
+  const char *line;		/* Line in the file.  */
+  const char *expected;		/* Expected output.  NULL if skipped.  */
+};
+
+const struct input inputs[] =
+  {
+   /* Regular entries.  */
+   { "g1:x1::\n", "g1:x1::\n" },
+   { "g2:x2:a1:\n", "g2:x2:a1:\n" },
+   { "g3:x3:a2:u1\n", "g3:x3:a2:u1\n" },
+   { "g4:x4:a3,a4:u2,u3,u4\n", "g4:x4:a3,a4:u2,u3,u4\n" },
+
+   /* Comments and empty lines.  */
+   { "\n", NULL },
+   { " \n", NULL },
+   { "\t\n", NULL },
+   { "#g:x::\n", NULL },
+   { " #g:x::\n", NULL },
+   { "\t#g:x::\n", NULL },
+   { " \t#g:x::\n", NULL },
+
+   /* Marker for synchronization.  */
+   { "g5:x5::\n", "g5:x5::\n" },
+
+   /* Leading whitespace.  */
+   { " g6:x6::\n", "g6:x6::\n" },
+   { "\tg7:x7::\n", "g7:x7::\n" },
+
+   /* This is expected to trigger buffer exhaustion during parsing
+      (bug 20338).  */
+   {
+    "g8:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:u5,u6,u7,u8,u9:\n",
+    "g8:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx:u5,u6,u7,u8,u9:\n",
+   },
+   {
+    "g9:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx::a5,a6,a7,a8,a9,a10\n",
+    "g9:xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx::a5,a6,a7,a8,a9,a10\n",
+   },
+  };
+
+/* Writes the test data to a temporary file and returns its name.  The
+   returned pointer should be freed.  */
+static char *
+create_test_file (void)
+{
+  char *path;
+  int fd = create_temp_file ("tst-fgetsgent_r-", &path);
+  FILE *fp = fdopen (fd, "w");
+  TEST_VERIFY_EXIT (fp != NULL);
+
+  for (size_t i = 0; i < array_length (inputs); ++i)
+    fputs (inputs[i].line, fp);
+
+  xfclose (fp);
+  return path;
+}
+
+/* Read the test file with the indicated start buffer size.  Return
+   true if the buffer size had to be increased during reading.  */
+static bool
+run_test (const char *path, size_t buffer_size)
+{
+  bool resized = false;
+  FILE *fp = xfopen (path, "r");
+
+  /* This avoids repeated lseek system calls (bug 26257).  */
+  TEST_COMPARE (fseeko64 (fp, 0, SEEK_SET), 0);
+
+  size_t i = 0;
+  while (true)
+    {
+      /* Skip over unused expected entries.  */
+      while (i < array_length (inputs) && inputs[i].expected == NULL)
+	++i;
+
+      /* Store the data on the heap, to help valgrind to detect
+	 invalid accesses.  */
+      struct sgrp *result_storage = xmalloc (sizeof (*result_storage));
+      char *buffer = xmalloc (buffer_size);
+      struct sgrp **result_pointer_storage
+	= xmalloc (sizeof (*result_pointer_storage));
+
+      int ret = fgetsgent_r (fp, result_storage, buffer, buffer_size,
+			     result_pointer_storage);
+      if (ret == 0)
+	{
+	  TEST_VERIFY (*result_pointer_storage != NULL);
+	  TEST_VERIFY (i < array_length (inputs));
+	  if (*result_pointer_storage != NULL
+	      && i < array_length (inputs))
+	    {
+	      char * actual = format_ent (*result_pointer_storage);
+	      TEST_COMPARE_STRING (inputs[i].expected, actual);
+	      free (actual);
+	      ++i;
+	    }
+	  else
+	    break;
+	}
+      else
+	{
+	  TEST_VERIFY (*result_pointer_storage == NULL);
+	  TEST_COMPARE (ret, errno);
+
+	  if (ret == ENOENT)
+	    {
+	      TEST_COMPARE (i, array_length (inputs));
+	      free (result_pointer_storage);
+	      free (buffer);
+	      free (result_storage);
+	      break;
+	    }
+	  else if (ret == ERANGE)
+	    {
+	      resized = true;
+	      ++buffer_size;
+	    }
+	  else
+	    FAIL_EXIT1 ("read failure: %m");
+	}
+
+      free (result_pointer_storage);
+      free (buffer);
+      free (result_storage);
+    }
+
+  xfclose (fp);
+  return resized;
+}
+
+static int
+do_test (void)
+{
+  char *path = create_test_file ();
+
+  for (size_t buffer_size = 3; ; ++buffer_size)
+    {
+      bool resized = run_test (path, buffer_size);
+      if (!resized)
+	break;
+    }
+
+  free (path);
+
+  return 0;
+}
+
+#include <support/test-driver.c>
diff --git a/nss/tst-gshadow.c b/nss/tst-gshadow.c
new file mode 100644
index 0000000000..8b469b723d
--- /dev/null
+++ b/nss/tst-gshadow.c
@@ -0,0 +1,141 @@
+#include <gshadow.h>
+#include <stdio.h>
+#include <string.h>
+
+
+static const struct sgrp data[] =
+  {
+    { (char *) "one", (char *) "pwdone",
+      (char *[]) { (char *) "admoneone", (char *) "admonetwo",
+		  (char *) "admonethree", NULL },
+      (char *[]) { (char *) "memoneone", (char *) "memonetwo",
+		  (char *) "memonethree", NULL } },
+    { (char *) "two", (char *) "pwdtwo",
+      (char *[]) { (char *) "admtwoone", (char *) "admtwotwo", NULL },
+      (char *[]) { (char *) "memtwoone", (char *) "memtwotwo",
+		  (char *) "memtwothree", NULL } },
+    { (char *) "three", (char *) "pwdthree",
+      (char *[]) { (char *) "admthreeone", (char *) "admthreetwo", NULL },
+      (char *[]) { (char *) "memthreeone", (char *) "memthreetwo", NULL } },
+    { (char *) "four", (char *) "pwdfour",
+      (char *[]) { (char *) "admfourone", (char *) "admfourtwo", NULL },
+      (char *[]) { NULL } },
+    { (char *) "five", (char *) "pwdfive",
+      (char *[]) { NULL },
+      (char *[]) { (char *) "memfiveone", (char *) "memfivetwo", NULL } },
+  };
+#define ndata (sizeof (data) / sizeof (data[0]))
+
+
+static int
+do_test (void)
+{
+  FILE *fp = tmpfile ();
+  if (fp == NULL)
+    {
+      puts ("cannot open temporary file");
+      return 1;
+    }
+
+  for (size_t i = 0; i < ndata; ++i)
+    if (putsgent (&data[i], fp) != 0)
+      {
+	printf ("putsgent call %zu failed\n", i + 1);
+	return 1;
+      }
+
+  rewind (fp);
+
+  int result = 0;
+  int seen = -1;
+  struct sgrp *g;
+  while ((g = fgetsgent (fp)) != NULL)
+    {
+      ++seen;
+      if (strcmp (g->sg_namp, data[seen].sg_namp) != 0)
+	{
+	  printf ("sg_namp of entry %d does not match: %s vs %s\n",
+		  seen + 1, g->sg_namp, data[seen].sg_namp);
+	  result = 1;
+	}
+      if (strcmp (g->sg_passwd, data[seen].sg_passwd) != 0)
+	{
+	  printf ("sg_passwd of entry %d does not match: %s vs %s\n",
+		  seen + 1, g->sg_passwd, data[seen].sg_passwd);
+	  result = 1;
+	}
+      if (g->sg_adm == NULL)
+	{
+	  printf ("sg_adm of entry %d is NULL\n", seen + 1);
+	  result = 1;
+	}
+      else
+	{
+	  int i = 1;
+	  char **sp1 = g->sg_adm;
+	  char **sp2 = data[seen].sg_adm;
+	  while (*sp1 != NULL && *sp2 != NULL)
+	    {
+	      if (strcmp (*sp1, *sp2) != 0)
+		{
+		  printf ("sg_adm[%d] of entry %d does not match: %s vs %s\n",
+			  i, seen + 1, *sp1, *sp2);
+		  result = 1;
+		}
+	      ++sp1;
+	      ++sp2;
+	      ++i;
+	    }
+	  if (*sp1 == NULL && *sp2 != NULL)
+	    {
+	      printf ("sg_adm of entry %d has too few entries\n", seen + 1);
+	      result = 1;
+	    }
+	  else if (*sp1 != NULL && *sp2 == NULL)
+	    {
+	      printf ("sg_adm of entry %d has too many entries\n", seen + 1);
+	      result = 1;
+	    }
+	}
+      if (g->sg_mem == NULL)
+	{
+	  printf ("sg_mem of entry %d is NULL\n", seen + 1);
+	  result = 1;
+	}
+      else
+	{
+	  int i = 1;
+	  char **sp1 = g->sg_mem;
+	  char **sp2 = data[seen].sg_mem;
+	  while (*sp1 != NULL && *sp2 != NULL)
+	    {
+	      if (strcmp (*sp1, *sp2) != 0)
+		{
+		  printf ("sg_mem[%d] of entry %d does not match: %s vs %s\n",
+			  i, seen + 1, *sp1, *sp2);
+		  result = 1;
+		}
+	      ++sp1;
+	      ++sp2;
+	      ++i;
+	    }
+	  if (*sp1 == NULL && *sp2 != NULL)
+	    {
+	      printf ("sg_mem of entry %d has too few entries\n", seen + 1);
+	      result = 1;
+	    }
+	  else if (*sp1 != NULL && *sp2 == NULL)
+	    {
+	      printf ("sg_mem of entry %d has too many entries\n", seen + 1);
+	      result = 1;
+	    }
+	}
+    }
+
+  fclose (fp);
+
+  return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/nss/tst-putsgent.c b/nss/tst-putsgent.c
new file mode 100644
index 0000000000..2ee0253e49
--- /dev/null
+++ b/nss/tst-putsgent.c
@@ -0,0 +1,168 @@
+/* Test for processing of invalid gshadow entries.  [BZ #18724]
+   Copyright (C) 2015-2023 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 <errno.h>
+#include <gshadow.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static bool errors;
+
+static void
+check (struct sgrp e, const char *expected)
+{
+  char *buf;
+  size_t buf_size;
+  FILE *f = open_memstream (&buf, &buf_size);
+
+  if (f == NULL)
+    {
+      printf ("open_memstream: %m\n");
+      errors = true;
+      return;
+    }
+
+  int ret = putsgent (&e, f);
+
+  if (expected == NULL)
+    {
+      if (ret == -1)
+	{
+	  if (errno != EINVAL)
+	    {
+	      printf ("putsgent: unexpected error code: %m\n");
+	      errors = true;
+	    }
+	}
+      else
+	{
+	  printf ("putsgent: unexpected success (\"%s\")\n", e.sg_namp);
+	  errors = true;
+	}
+    }
+  else
+    {
+      /* Expect success.  */
+      size_t expected_length = strlen (expected);
+      if (ret == 0)
+	{
+	  long written = ftell (f);
+
+	  if (written <= 0 || fflush (f) < 0)
+	    {
+	      printf ("stream error: %m\n");
+	      errors = true;
+	    }
+	  else if (buf[written - 1] != '\n')
+	    {
+	      printf ("FAILED: \"%s\" without newline\n", expected);
+	      errors = true;
+	    }
+	  else if (strncmp (buf, expected, written - 1) != 0
+		   || written - 1 != expected_length)
+	    {
+	      printf ("FAILED: \"%s\" (%ld), expected \"%s\" (%zu)\n",
+		      buf, written - 1, expected, expected_length);
+	      errors = true;
+	    }
+	}
+      else
+	{
+	  printf ("FAILED: putsgent (expected \"%s\"): %m\n", expected);
+	  errors = true;
+	}
+    }
+
+  fclose (f);
+  free (buf);
+}
+
+static int
+do_test (void)
+{
+  check ((struct sgrp) {
+      .sg_namp = (char *) "root",
+    },
+    "root:::");
+  check ((struct sgrp) {
+      .sg_namp = (char *) "root",
+      .sg_passwd = (char *) "password",
+    },
+    "root:password::");
+  check ((struct sgrp) {
+      .sg_namp = (char *) "root",
+      .sg_passwd = (char *) "password",
+      .sg_adm = (char *[]) {(char *) "adm1", (char *) "adm2", NULL},
+      .sg_mem = (char *[]) {(char *) "mem1", (char *) "mem2", NULL},
+    },
+    "root:password:adm1,adm2:mem1,mem2");
+
+  /* Bad values.  */
+  {
+    static const char *const bad_strings[] = {
+      ":",
+      "\n",
+      ":bad",
+      "\nbad",
+      "b:ad",
+      "b\nad",
+      "bad:",
+      "bad\n",
+      "b:a\nd",
+      ",",
+      "\n,",
+      ":,",
+      ",bad",
+      "b,ad",
+      "bad,",
+      NULL
+    };
+    for (const char *const *bad = bad_strings; *bad != NULL; ++bad)
+      {
+	char *members[]
+	  = {(char *) "first", (char *) *bad, (char *) "last", NULL};
+	if (strpbrk (*bad, ":\n") != NULL)
+	  {
+	    check ((struct sgrp) {
+		.sg_namp = (char *) *bad,
+	      }, NULL);
+	    check ((struct sgrp) {
+		.sg_namp = (char *) "root",
+		.sg_passwd = (char *) *bad,
+	      }, NULL);
+	  }
+	check ((struct sgrp) {
+	    .sg_namp = (char *) "root",
+	    .sg_passwd = (char *) "password",
+	    .sg_adm = members
+	  }, NULL);
+	check ((struct sgrp) {
+	    .sg_namp = (char *) "root",
+	    .sg_passwd = (char *) "password",
+	    .sg_mem = members
+	  }, NULL);
+      }
+  }
+
+  return errors;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/nss/tst-sgetsgent.c b/nss/tst-sgetsgent.c
new file mode 100644
index 0000000000..0370c10fd0
--- /dev/null
+++ b/nss/tst-sgetsgent.c
@@ -0,0 +1,69 @@
+/* Test large input for sgetsgent (bug 30151).
+   Copyright (C) 2023 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 <gshadow.h>
+#include <stddef.h>
+#include <support/check.h>
+#include <support/support.h>
+#include <support/xmemstream.h>
+#include <stdlib.h>
+
+static int
+do_test (void)
+{
+  /* Create a shadow group with 1000 members.  */
+  struct xmemstream mem;
+  xopen_memstream (&mem);
+  const char *passwd = "k+zD0nucwfxAo3sw1NXUj6K5vt5M16+X0TVGdE1uFvq5R8V7efJ";
+  fprintf (mem.out, "group-name:%s::m0", passwd);
+  for (int i = 1; i < 1000; ++i)
+    fprintf (mem.out, ",m%d", i);
+  xfclose_memstream (&mem);
+
+  /* Call sgetsgent.  */
+  char *input = mem.buffer;
+  struct sgrp *e = sgetsgent (input);
+  TEST_VERIFY_EXIT (e != NULL);
+  TEST_COMPARE_STRING (e->sg_namp, "group-name");
+  TEST_COMPARE_STRING (e->sg_passwd, passwd);
+  /* No administrators.  */
+  TEST_COMPARE_STRING (e->sg_adm[0], NULL);
+  /* Check the members list.  */
+  for (int i = 0; i < 1000; ++i)
+    {
+      char *member = xasprintf ("m%d", i);
+      TEST_COMPARE_STRING (e->sg_mem[i], member);
+      free (member);
+    }
+  TEST_COMPARE_STRING (e->sg_mem[1000], NULL);
+
+  /* Check that putsgent brings back the input string.  */
+  xopen_memstream (&mem);
+  TEST_COMPARE (putsgent (e, mem.out), 0);
+  xfclose_memstream (&mem);
+  /* Compare without the trailing '\n' that putsgent added.  */
+  TEST_COMPARE (mem.buffer[mem.length - 1], '\n');
+  mem.buffer[mem.length - 1] = '\0';
+  TEST_COMPARE_STRING (mem.buffer, input);
+
+  free (mem.buffer);
+  free (input);
+  return 0;
+}
+
+#include <support/test-driver.c>