diff options
Diffstat (limited to 'nss')
-rw-r--r-- | nss/Makefile | 27 | ||||
-rw-r--r-- | nss/Versions | 13 | ||||
-rw-r--r-- | nss/fgetspent.c | 90 | ||||
-rw-r--r-- | nss/fgetspent_r.c | 46 | ||||
-rw-r--r-- | nss/getspent.c | 31 | ||||
-rw-r--r-- | nss/getspent_r.c | 31 | ||||
-rw-r--r-- | nss/getspnam.c | 31 | ||||
-rw-r--r-- | nss/getspnam_r.c | 31 | ||||
-rw-r--r-- | nss/lckpwdf.c | 175 | ||||
-rw-r--r-- | nss/putspent.c | 94 | ||||
-rw-r--r-- | nss/sgetspent.c | 77 | ||||
-rw-r--r-- | nss/sgetspent_r.c | 103 | ||||
-rw-r--r-- | nss/shadow.h | 156 | ||||
-rw-r--r-- | nss/tst-putspent.c | 164 | ||||
-rw-r--r-- | nss/tst-shadow.c | 84 |
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" |