about summary refs log tree commit diff
path: root/nss
diff options
context:
space:
mode:
Diffstat (limited to 'nss')
-rw-r--r--nss/Makefile27
-rw-r--r--nss/Versions13
-rw-r--r--nss/fgetspent.c90
-rw-r--r--nss/fgetspent_r.c46
-rw-r--r--nss/getspent.c31
-rw-r--r--nss/getspent_r.c31
-rw-r--r--nss/getspnam.c31
-rw-r--r--nss/getspnam_r.c31
-rw-r--r--nss/lckpwdf.c175
-rw-r--r--nss/putspent.c94
-rw-r--r--nss/sgetspent.c77
-rw-r--r--nss/sgetspent_r.c103
-rw-r--r--nss/shadow.h156
-rw-r--r--nss/tst-putspent.c164
-rw-r--r--nss/tst-shadow.c84
15 files changed, 1153 insertions, 0 deletions
diff --git a/nss/Makefile b/nss/Makefile
index 28648ea884..84cf62af2b 100644
--- a/nss/Makefile
+++ b/nss/Makefile
@@ -27,6 +27,7 @@ headers := \
   gshadow.h \
   nss.h \
   pwd.h \
+  shadow.h \
   # headers
 
 # This is the trivial part which goes into libc itself.
@@ -125,6 +126,30 @@ CFLAGS-getpwent.c += -fexceptions
 CFLAGS-getpwent_r.c += -fexceptions
 endif
 
+# shadow routines
+routines += \
+  fgetspent \
+  fgetspent_r \
+  getspent \
+  getspent_r \
+  getspnam \
+  getspnam_r \
+  lckpwdf \
+  putspent \
+  sgetspent \
+  sgetspent_r \
+  # routines
+
+ifeq ($(have-thread-library),yes)
+CFLAGS-getspent_r.c += -fexceptions
+CFLAGS-getspent.c += -fexceptions
+CFLAGS-fgetspent.c += -fexceptions
+CFLAGS-fgetspent_r.c += -fexceptions $(libio-mtsafe)
+CFLAGS-putspent.c += -fexceptions $(libio-mtsafe)
+CFLAGS-getspnam.c += -fexceptions
+CFLAGS-getspnam_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.
@@ -177,7 +202,9 @@ tests := \
   tst-putgrent \
   tst-putpwent \
   tst-putsgent \
+  tst-putspent \
   tst-sgetsgent \
+  tst-shadow \
   # tests
 
 xtests = bug-erange
diff --git a/nss/Versions b/nss/Versions
index 58ca73c9df..632af25be4 100644
--- a/nss/Versions
+++ b/nss/Versions
@@ -9,25 +9,37 @@ libc {
     # e*
     endgrent;
     endpwent;
+    endspent;
 
     # f*
     fgetgrent; fgetgrent_r;
     fgetpwent; fgetpwent_r;
+    fgetspent; fgetspent_r;
 
     # g*
     getgrent; getgrent_r; getgrgid; getgrgid_r; getgrnam; getgrnam_r;
     getgroups;
     getpw; getpwent; getpwent_r; getpwnam; getpwnam_r; getpwuid; getpwuid_r;
+    getspent; getspent_r; getspnam; getspnam_r;
 
     # i*
     initgroups;
 
+    # l*
+    lckpwdf;
+
     # p*
     putpwent;
+    putspent;
 
     # s*
     setgrent;
     setpwent;
+    setspent;
+    sgetspent; sgetspent_r;
+
+    # u*
+    ulckpwdf;
   }
   GLIBC_2.1 {
     # p*
@@ -37,6 +49,7 @@ libc {
     # g*
     getgrent_r; getgrgid_r; getgrnam_r;
     getpwent_r; getpwuid_r; getpwnam_r;
+    getspent_r; getspnam_r;
   }
   GLIBC_2.2.2 {
     __nss_hostname_digits_dots;
diff --git a/nss/fgetspent.c b/nss/fgetspent.c
new file mode 100644
index 0000000000..541947bad6
--- /dev/null
+++ b/nss/fgetspent.c
@@ -0,0 +1,90 @@
+/* Copyright (C) 1996-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 <shadow.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 spwd *
+fgetspent (FILE *stream)
+{
+  static size_t buffer_size;
+  static struct spwd resbuf;
+  fpos_t pos;
+  struct spwd *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
+	 && (__fgetspent_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_fgetspent_freemem_ptr);
diff --git a/nss/fgetspent_r.c b/nss/fgetspent_r.c
new file mode 100644
index 0000000000..5323914175
--- /dev/null
+++ b/nss/fgetspent_r.c
@@ -0,0 +1,46 @@
+/* Copyright (C) 1996-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 <shadow.h>
+#include <stdio.h>
+
+/* Define a line parsing function using the common code
+   used in the nss_files module.  */
+
+#define STRUCTURE	spwd
+#define ENTNAME		spent
+#define	EXTERN_PARSER	1
+struct spent_data {};
+
+#include <nss/nss_files/files-parse.c>
+
+
+/* Read one shadow entry from the given stream.  */
+int
+__fgetspent_r (FILE *stream, struct spwd *resbuf, char *buffer, size_t buflen,
+	       struct spwd **result)
+{
+  int ret = __nss_fgetent_r (stream, resbuf, buffer, buflen, parse_line);
+  if (ret == 0)
+    *result = resbuf;
+  else
+    *result = NULL;
+  return ret;
+}
+weak_alias (__fgetspent_r, fgetspent_r)
diff --git a/nss/getspent.c b/nss/getspent.c
new file mode 100644
index 0000000000..d2109f8b16
--- /dev/null
+++ b/nss/getspent.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 1996-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 <shadow.h>
+
+
+#define LOOKUP_TYPE	struct spwd
+#define SETFUNC_NAME	setspent
+#define	GETFUNC_NAME	getspent
+#define	ENDFUNC_NAME	endspent
+#define DATABASE_NAME	shadow
+#define BUFLEN		1024
+
+/* There is no nscd support for the shadow file.  */
+#undef	USE_NSCD
+
+#include "../nss/getXXent.c"
diff --git a/nss/getspent_r.c b/nss/getspent_r.c
new file mode 100644
index 0000000000..f63e44f49b
--- /dev/null
+++ b/nss/getspent_r.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 1996-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 <shadow.h>
+
+
+#define LOOKUP_TYPE		struct spwd
+#define SETFUNC_NAME		setspent
+#define	GETFUNC_NAME		getspent
+#define	ENDFUNC_NAME		endspent
+#define DATABASE_NAME		shadow
+#define BUFLEN			1024
+
+/* There is no nscd support for the shadow file.  */
+#undef	USE_NSCD
+
+#include "../nss/getXXent_r.c"
diff --git a/nss/getspnam.c b/nss/getspnam.c
new file mode 100644
index 0000000000..889e11c95b
--- /dev/null
+++ b/nss/getspnam.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 1996-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 <shadow.h>
+
+
+#define LOOKUP_TYPE	struct spwd
+#define FUNCTION_NAME	getspnam
+#define DATABASE_NAME	shadow
+#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/getspnam_r.c b/nss/getspnam_r.c
new file mode 100644
index 0000000000..381362f41e
--- /dev/null
+++ b/nss/getspnam_r.c
@@ -0,0 +1,31 @@
+/* Copyright (C) 1996-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 <shadow.h>
+
+
+#define LOOKUP_TYPE		struct spwd
+#define FUNCTION_NAME		getspnam
+#define DATABASE_NAME		shadow
+#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_r.c"
diff --git a/nss/lckpwdf.c b/nss/lckpwdf.c
new file mode 100644
index 0000000000..3b36b2ebca
--- /dev/null
+++ b/nss/lckpwdf.c
@@ -0,0 +1,175 @@
+/* Handle locking of password file.
+   Copyright (C) 1996-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 <fcntl.h>
+#include <libc-lock.h>
+#include <shadow.h>
+#include <signal.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sigsetops.h>
+
+#include <kernel-features.h>
+
+
+/* Name of the lock file.  */
+#define PWD_LOCKFILE "/etc/.pwd.lock"
+
+/* How long to wait for getting the lock before returning with an
+   error.  */
+#define TIMEOUT 15 /* sec */
+
+
+/* File descriptor for lock file.  */
+static int lock_fd = -1;
+
+/* Prevent problems in multithreaded program by using mutex.  */
+__libc_lock_define_initialized (static, lock)
+
+
+/* Prototypes for local functions.  */
+static void noop_handler (int __sig);
+
+
+/* We cannot simply return in error cases.  We have to close the file
+   and perhaps restore the signal handler.  */
+#define RETURN_CLOSE_FD(code)						      \
+  do {									      \
+    if ((code) < 0 && lock_fd >= 0)					      \
+      {									      \
+	__close (lock_fd);						      \
+	lock_fd = -1;							      \
+      }									      \
+    __libc_lock_unlock (lock);						      \
+    return (code);							      \
+  } while (0)
+
+#define RETURN_RESTORE_HANDLER(code)					      \
+  do {									      \
+    /* Restore old action handler for alarm.  We don't need to know	      \
+       about the current one.  */					      \
+    __sigaction (SIGALRM, &saved_act, NULL);				      \
+    RETURN_CLOSE_FD (code);						      \
+  } while (0)
+
+#define RETURN_CLEAR_ALARM(code)					      \
+  do {									      \
+    /* Clear alarm.  */							      \
+    alarm (0);								      \
+    /* Restore old set of handled signals.  We don't need to know	      \
+       about the current one.*/						      \
+    __sigprocmask (SIG_SETMASK, &saved_set, NULL);			      \
+    RETURN_RESTORE_HANDLER (code);					      \
+  } while (0)
+
+
+int
+__lckpwdf (void)
+{
+  sigset_t saved_set;			/* Saved set of caught signals.  */
+  struct sigaction saved_act;		/* Saved signal action.  */
+  sigset_t new_set;			/* New set of caught signals.  */
+  struct sigaction new_act;		/* New signal action.  */
+  struct flock fl;			/* Information struct for locking.  */
+  int result;
+
+  if (lock_fd != -1)
+    /* Still locked by own process.  */
+    return -1;
+
+  /* Prevent problems caused by multiple threads.  */
+  __libc_lock_lock (lock);
+
+  int oflags = O_WRONLY | O_CREAT | O_CLOEXEC;
+  lock_fd = __open (PWD_LOCKFILE, oflags, 0600);
+  if (lock_fd == -1)
+    /* Cannot create lock file.  */
+    RETURN_CLOSE_FD (-1);
+
+  /* Now we have to get exclusive write access.  Since multiple
+     process could try this we won't stop when it first fails.
+     Instead we set a timeout for the system call.  Once the timer
+     expires it is likely that there are some problems which cannot be
+     resolved by waiting.
+
+     It is important that we don't change the signal state.  We must
+     restore the old signal behaviour.  */
+  memset (&new_act, '\0', sizeof (struct sigaction));
+  new_act.sa_handler = noop_handler;
+  __sigfillset (&new_act.sa_mask);
+  new_act.sa_flags = 0ul;
+
+  /* Install new action handler for alarm and save old.  */
+  if (__sigaction (SIGALRM, &new_act, &saved_act) < 0)
+    /* Cannot install signal handler.  */
+    RETURN_CLOSE_FD (-1);
+
+  /* Now make sure the alarm signal is not blocked.  */
+  __sigemptyset (&new_set);
+  __sigaddset (&new_set, SIGALRM);
+  if (__sigprocmask (SIG_UNBLOCK, &new_set, &saved_set) < 0)
+    RETURN_RESTORE_HANDLER (-1);
+
+  /* Start timer.  If we cannot get the lock in the specified time we
+     get a signal.  */
+  alarm (TIMEOUT);
+
+  /* Try to get the lock.  */
+  memset (&fl, '\0', sizeof (struct flock));
+  fl.l_type = F_WRLCK;
+  fl.l_whence = SEEK_SET;
+  result = __fcntl (lock_fd, F_SETLKW, &fl);
+
+  RETURN_CLEAR_ALARM (result);
+}
+weak_alias (__lckpwdf, lckpwdf)
+
+
+int
+__ulckpwdf (void)
+{
+  int result;
+
+  if (lock_fd == -1)
+    /* There is no lock set.  */
+    result = -1;
+  else
+    {
+      /* Prevent problems caused by multiple threads.  */
+      __libc_lock_lock (lock);
+
+      result = __close (lock_fd);
+
+      /* Mark descriptor as unused.  */
+      lock_fd = -1;
+
+      /* Clear mutex.  */
+      __libc_lock_unlock (lock);
+    }
+
+  return result;
+}
+weak_alias (__ulckpwdf, ulckpwdf)
+
+
+static void
+noop_handler (int sig)
+{
+  /* We simply return which makes the `fcntl' call return with an error.  */
+}
diff --git a/nss/putspent.c b/nss/putspent.c
new file mode 100644
index 0000000000..a80711d0b6
--- /dev/null
+++ b/nss/putspent.c
@@ -0,0 +1,94 @@
+/* Copyright (C) 1991-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 <nss.h>
+#include <stdio.h>
+#include <shadow.h>
+
+#define flockfile(s) _IO_flockfile (s)
+#define funlockfile(s) _IO_funlockfile (s)
+
+#define _S(x)	x ? x : ""
+
+
+/* Write an entry to the given stream.
+   This must know the format of the password file.  */
+int
+putspent (const struct spwd *p, FILE *stream)
+{
+  int errors = 0;
+
+  if (p->sp_namp == NULL || !__nss_valid_field (p->sp_namp)
+      || !__nss_valid_field (p->sp_pwdp))
+    {
+      __set_errno (EINVAL);
+      return -1;
+    }
+
+  flockfile (stream);
+
+  if (fprintf (stream, "%s:%s:", p->sp_namp, _S (p->sp_pwdp)) < 0)
+    ++errors;
+
+  if ((p->sp_lstchg != (long int) -1
+       && fprintf (stream, "%ld:", p->sp_lstchg) < 0)
+      || (p->sp_lstchg == (long int) -1
+	  && putc_unlocked (':', stream) == EOF))
+    ++errors;
+
+  if ((p->sp_min != (long int) -1
+       && fprintf (stream, "%ld:", p->sp_min) < 0)
+      || (p->sp_min == (long int) -1
+	  && putc_unlocked (':', stream) == EOF))
+    ++errors;
+
+  if ((p->sp_max != (long int) -1
+       && fprintf (stream, "%ld:", p->sp_max) < 0)
+      || (p->sp_max == (long int) -1
+	  && putc_unlocked (':', stream) == EOF))
+    ++errors;
+
+  if ((p->sp_warn != (long int) -1
+       && fprintf (stream, "%ld:", p->sp_warn) < 0)
+      || (p->sp_warn == (long int) -1
+	  && putc_unlocked (':', stream) == EOF))
+    ++errors;
+
+  if ((p->sp_inact != (long int) -1
+       && fprintf (stream, "%ld:", p->sp_inact) < 0)
+      || (p->sp_inact == (long int) -1
+	  && putc_unlocked (':', stream) == EOF))
+    ++errors;
+
+  if ((p->sp_expire != (long int) -1
+       && fprintf (stream, "%ld:", p->sp_expire) < 0)
+      || (p->sp_expire == (long int) -1
+	  && putc_unlocked (':', stream) == EOF))
+    ++errors;
+
+  if (p->sp_flag != ~0ul
+      && fprintf (stream, "%ld", p->sp_flag) < 0)
+    ++errors;
+
+  if (putc_unlocked ('\n', stream) == EOF)
+    ++errors;
+
+  funlockfile (stream);
+
+  return errors ? -1 : 0;
+}
diff --git a/nss/sgetspent.c b/nss/sgetspent.c
new file mode 100644
index 0000000000..99c0973fdd
--- /dev/null
+++ b/nss/sgetspent.c
@@ -0,0 +1,77 @@
+/* Copyright (C) 1996-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 <shadow.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 spwd *
+sgetspent (const char *string)
+{
+  static char *buffer;
+  static size_t buffer_size;
+  static struct spwd resbuf;
+  struct spwd *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
+	 && (__sgetspent_r (string, &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;
+    }
+
+  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/sgetspent_r.c b/nss/sgetspent_r.c
new file mode 100644
index 0000000000..cb51db7106
--- /dev/null
+++ b/nss/sgetspent_r.c
@@ -0,0 +1,103 @@
+/* Copyright (C) 1996-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 <shadow.h>
+#include <stdio.h>
+#include <string.h>
+
+/* Define a line parsing function using the common code
+   used in the nss_files module.  */
+
+#define STRUCTURE	spwd
+#define ENTNAME		spent
+struct spent_data {};
+
+/* Predicate which always returns false, needed below.  */
+#define FALSEP(arg) 0
+
+
+#include <nss/nss_files/files-parse.c>
+LINE_PARSER
+(,
+ STRING_FIELD (result->sp_namp, ISCOLON, 0);
+ if (line[0] == '\0'
+     && (result->sp_namp[0] == '+' || result->sp_namp[0] == '-'))
+   {
+     result->sp_pwdp = NULL;
+     result->sp_lstchg = 0;
+     result->sp_min = 0;
+     result->sp_max = 0;
+     result->sp_warn = -1l;
+     result->sp_inact = -1l;
+     result->sp_expire = -1l;
+     result->sp_flag = ~0ul;
+   }
+ else
+   {
+     STRING_FIELD (result->sp_pwdp, ISCOLON, 0);
+     INT_FIELD_MAYBE_NULL (result->sp_lstchg, ISCOLON, 0, 10, (long int) (int),
+			   (long int) -1);
+     INT_FIELD_MAYBE_NULL (result->sp_min, ISCOLON, 0, 10, (long int) (int),
+			   (long int) -1);
+     INT_FIELD_MAYBE_NULL (result->sp_max, ISCOLON, 0, 10, (long int) (int),
+			   (long int) -1);
+     while (isspace (*line))
+       ++line;
+     if (*line == '\0')
+       {
+	 /* The old form.  */
+	 result->sp_warn = -1l;
+	 result->sp_inact = -1l;
+	 result->sp_expire = -1l;
+	 result->sp_flag = ~0ul;
+       }
+     else
+       {
+	 INT_FIELD_MAYBE_NULL (result->sp_warn, ISCOLON, 0, 10,
+			       (long int) (int), (long int) -1);
+	 INT_FIELD_MAYBE_NULL (result->sp_inact, ISCOLON, 0, 10,
+			       (long int) (int), (long int) -1);
+	 INT_FIELD_MAYBE_NULL (result->sp_expire, ISCOLON, 0, 10,
+			       (long int) (int), (long int) -1);
+	 if (*line != '\0')
+	   INT_FIELD_MAYBE_NULL (result->sp_flag, FALSEP, 0, 10,
+				 (unsigned long int), ~0ul)
+	 else
+	   result->sp_flag = ~0ul;
+       }
+   }
+ )
+
+
+/* Read one shadow entry from the given stream.  */
+int
+__sgetspent_r (const char *string, struct spwd *resbuf, char *buffer,
+	       size_t buflen, struct spwd **result)
+{
+  buffer[buflen - 1] = '\0';
+  char *sp = strncpy (buffer, string, buflen);
+  if (buffer[buflen - 1] != '\0')
+    return ERANGE;
+
+  int parse_result = parse_line (sp, resbuf, NULL, 0, &errno);
+  *result = parse_result > 0 ? resbuf : NULL;
+
+  return *result == NULL ? errno : 0;
+}
+weak_alias (__sgetspent_r, sgetspent_r)
diff --git a/nss/shadow.h b/nss/shadow.h
new file mode 100644
index 0000000000..76d1cd29ce
--- /dev/null
+++ b/nss/shadow.h
@@ -0,0 +1,156 @@
+/* Copyright (C) 1996-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" storage of hashed
+   passphrases.  The shadow database is like the user database, but is
+   only accessible with special privileges, so that malicious users
+   cannot retrieve everyone else's hashed passphrase to brute-force at
+   their convenience.  */
+
+#ifndef _SHADOW_H
+#define _SHADOW_H	1
+
+#include <features.h>
+
+#include <paths.h>
+
+#define __need_size_t
+#include <stddef.h>
+
+#include <bits/types/FILE.h>
+
+/* Paths to the user database files.  */
+#define	SHADOW _PATH_SHADOW
+
+
+__BEGIN_DECLS
+
+/* A record in the shadow database.  */
+struct spwd
+  {
+    char *sp_namp;		/* Login name.  */
+    char *sp_pwdp;		/* Hashed passphrase.  */
+    long int sp_lstchg;		/* Date of last change.  */
+    long int sp_min;		/* Minimum number of days between changes.  */
+    long int sp_max;		/* Maximum number of days between changes.  */
+    long int sp_warn;		/* Number of days to warn user to change
+				   the password.  */
+    long int sp_inact;		/* Number of days the account may be
+				   inactive.  */
+    long int sp_expire;		/* Number of days since 1970-01-01 until
+				   account expires.  */
+    unsigned long int sp_flag;	/* Reserved.  */
+  };
+
+
+/* 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 setspent (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 endspent (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 spwd *getspent (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 spwd *getspnam (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 spwd *sgetspent (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 spwd *fgetspent (FILE *__stream);
+
+/* Write line containing shadow 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 putspent (const struct spwd *__p, 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 getspent_r (struct spwd *__result_buf, char *__buffer,
+		       size_t __buflen, struct spwd **__result)
+	__attr_access ((__write_only__, 2, 3));
+
+extern int getspnam_r (const char *__name, struct spwd *__result_buf,
+		       char *__buffer, size_t __buflen,
+		       struct spwd **__result)
+	__attr_access ((__write_only__, 3, 4));
+
+extern int sgetspent_r (const char *__string, struct spwd *__result_buf,
+			char *__buffer, size_t __buflen,
+			struct spwd **__result)
+	__attr_access ((__write_only__, 3, 4));
+
+extern int fgetspent_r (FILE *__stream, struct spwd *__result_buf,
+			char *__buffer, size_t __buflen,
+			struct spwd **__result)
+	__attr_access ((__write_only__, 3, 4));
+#endif	/* misc */
+
+
+/* The simple locking functionality provided here is not suitable for
+   multi-threaded applications.  */
+
+/* Request exclusive access to /etc/passwd and /etc/shadow.  */
+extern int lckpwdf (void) __THROW;
+
+/* Release exclusive access to /etc/passwd and /etc/shadow.  */
+extern int ulckpwdf (void) __THROW;
+
+__END_DECLS
+
+#endif /* shadow.h */
diff --git a/nss/tst-putspent.c b/nss/tst-putspent.c
new file mode 100644
index 0000000000..7b85096636
--- /dev/null
+++ b/nss/tst-putspent.c
@@ -0,0 +1,164 @@
+/* Test for processing of invalid shadow 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 <shadow.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+static bool errors;
+
+static void
+check (struct spwd p, 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 = putspent (&p, f);
+
+  if (expected == NULL)
+    {
+      if (ret == -1)
+	{
+	  if (errno != EINVAL)
+	    {
+	      printf ("putspent: unexpected error code: %m\n");
+	      errors = true;
+	    }
+	}
+      else
+	{
+	  printf ("putspent: unexpected success (\"%s\")\n", p.sp_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: putspent (expected \"%s\"): %m\n", expected);
+	  errors = true;
+	}
+    }
+
+  fclose (f);
+  free (buf);
+}
+
+static int
+do_test (void)
+{
+  check ((struct spwd) {
+      .sp_namp = (char *) "root",
+    },
+    "root::0:0:0:0:0:0:0");
+  check ((struct spwd) {
+      .sp_namp = (char *) "root",
+      .sp_pwdp = (char *) "password",
+    },
+    "root:password:0:0:0:0:0:0:0");
+  check ((struct spwd) {
+      .sp_namp = (char *) "root",
+      .sp_pwdp = (char *) "password",
+      .sp_lstchg = -1,
+      .sp_min = -1,
+      .sp_max = -1,
+      .sp_warn = -1,
+      .sp_inact = -1,
+      .sp_expire = -1,
+      .sp_flag = -1
+    },
+    "root:password:::::::");
+  check ((struct spwd) {
+      .sp_namp = (char *) "root",
+      .sp_pwdp = (char *) "password",
+      .sp_lstchg = 1,
+      .sp_min = 2,
+      .sp_max = 3,
+      .sp_warn = 4,
+      .sp_inact = 5,
+      .sp_expire = 6,
+      .sp_flag = 7
+    },
+    "root:password:1:2:3:4:5:6:7");
+
+  /* Bad values.  */
+  {
+    static const char *const bad_strings[] = {
+      ":",
+      "\n",
+      ":bad",
+      "\nbad",
+      "b:ad",
+      "b\nad",
+      "bad:",
+      "bad\n",
+      "b:a\nd",
+      NULL
+    };
+    for (const char *const *bad = bad_strings; *bad != NULL; ++bad)
+      {
+	check ((struct spwd) {
+	    .sp_namp = (char *) *bad,
+	  }, NULL);
+	check ((struct spwd) {
+	    .sp_namp = (char *) "root",
+	    .sp_pwdp = (char *) *bad,
+	  }, NULL);
+      }
+  }
+
+  return errors;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"
diff --git a/nss/tst-shadow.c b/nss/tst-shadow.c
new file mode 100644
index 0000000000..48f7167baa
--- /dev/null
+++ b/nss/tst-shadow.c
@@ -0,0 +1,84 @@
+#include <shadow.h>
+#include <stdio.h>
+#include <string.h>
+
+
+static const struct spwd data[] =
+  {
+    { (char *) "one", (char *) "pwdone", 1, 2, 3, 4, 5, 6, 7 },
+    { (char *) "two", (char *) "pwdtwo", 11, 12, 13, 14, 15, 16, 17 },
+    { (char *) "three", (char *) "pwdthree", -1, 22, 23, 24, 25, 26, 27 },
+    { (char *) "four", (char *) "pwdfour", 31, -1, 33, 34, 35, 36, 37 },
+    { (char *) "five", (char *) "pwdfive", 41, 42, -1, 44, 45, 46, 47 },
+    { (char *) "six", (char *) "pwdsix", 51, 52, 53, -1, 55, 56, 57 },
+    { (char *) "seven", (char *) "pwdseven", 61, 62, 63, 64, -1, 66, 67 },
+    { (char *) "eight", (char *) "pwdeigth", 71, 72, 73, 74, 75, -1, 77 },
+    { (char *) "nine", (char *) "pwdnine", 81, 82, 83, 84, 85, 86, ~0ul },
+  };
+#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 (putspent (&data[i], fp) != 0)
+      {
+	printf ("putspent call %zu failed\n", i + 1);
+	return 1;
+      }
+
+  rewind (fp);
+
+  int result = 0;
+  int seen = -1;
+  struct spwd *p;
+  while ((p = fgetspent (fp)) != NULL)
+    {
+      ++seen;
+      if (strcmp (p->sp_namp, data[seen].sp_namp) != 0)
+	{
+	  printf ("sp_namp of entry %d does not match: %s vs %s\n",
+		  seen + 1, p->sp_namp, data[seen].sp_namp);
+	  result = 1;
+	}
+      if (strcmp (p->sp_pwdp, data[seen].sp_pwdp) != 0)
+	{
+	  printf ("sp_pwdp of entry %d does not match: %s vs %s\n",
+		  seen + 1, p->sp_pwdp, data[seen].sp_pwdp);
+	  result = 1;
+	}
+#define T(f) \
+      if (p->f != data[seen].f)						      \
+	{								      \
+	  printf ("%s of entry %d wrong: %ld vs %ld\n",			      \
+		  #f, seen + 1, p->f, data[seen].f);			      \
+	  result = 1;							      \
+	}
+      T (sp_lstchg);
+      T (sp_min);
+      T (sp_max);
+      T (sp_warn);
+      T (sp_expire);
+      if (p->sp_flag != data[seen].sp_flag)
+	{
+	  printf ("sp_flag of entry %d wrong: %lu vs %lu\n",
+		  seen + 1, p->sp_flag, data[seen].sp_flag);
+	  result = 1;
+	}
+    }
+
+  fclose (fp);
+
+  return result;
+}
+
+#define TEST_FUNCTION do_test ()
+#include "../test-skeleton.c"