From b121fdc552f392cd86b21f159dd3e3b998de91a3 Mon Sep 17 00:00:00 2001 From: Arjun Shankar Date: Mon, 2 Oct 2023 14:55:15 +0200 Subject: Remove 'grp' and merge into 'nss' and 'posix' The majority of grp routines are entry points for nss functionality. This commit removes the 'grp' subdirectory and moves all nss-relevant functionality and all tests to 'nss', and the 'setgroups' stub into 'posix' (alongside the 'getgroups' stub). References to grp/ are accordingly changed. In addition, compat-initgroups.c, a fallback implementation of initgroups is renamed to initgroups-fallback.c so that the build system does not confuse it for nss_compat/compat-initgroups.c. Build time improves very slightly; e.g. down from an average of 45.5s to 44.5s on an 8-thread mobile x86_64 CPU. Reviewed-by: Adhemerval Zanella --- nss/Makefile | 43 +++++- nss/Versions | 30 ++++ nss/fgetgrent.c | 87 ++++++++++++ nss/fgetgrent_r.c | 65 +++++++++ nss/getgrent.c | 28 ++++ nss/getgrent_r.c | 28 ++++ nss/getgrgid.c | 28 ++++ nss/getgrgid_r.c | 31 ++++ nss/getgrnam.c | 28 ++++ nss/getgrnam_r.c | 31 ++++ nss/grp-merge.c | 200 ++++++++++++++++++++++++++ nss/grp-merge.h | 35 +++++ nss/grp.h | 207 +++++++++++++++++++++++++++ nss/initgroups-fallback.c | 116 +++++++++++++++ nss/initgroups.c | 218 +++++++++++++++++++++++++++++ nss/putgrent.c | 76 ++++++++++ nss/testgrp.c | 41 ++++++ nss/tst-initgroups1.c | 56 ++++++++ nss/tst-initgroups1.root/etc/group | 7 + nss/tst-initgroups1.root/etc/nsswitch.conf | 1 + nss/tst-initgroups1.root/etc/passwd | 1 + nss/tst-initgroups2.c | 21 +++ nss/tst-initgroups2.root/etc/group | 7 + nss/tst-initgroups2.root/etc/nsswitch.conf | 2 + nss/tst-initgroups2.root/etc/passwd | 1 + nss/tst-putgrent.c | 167 ++++++++++++++++++++++ nss/tst_fgetgrent.c | 126 +++++++++++++++++ nss/tst_fgetgrent.sh | 40 ++++++ 28 files changed, 1720 insertions(+), 1 deletion(-) create mode 100644 nss/fgetgrent.c create mode 100644 nss/fgetgrent_r.c create mode 100644 nss/getgrent.c create mode 100644 nss/getgrent_r.c create mode 100644 nss/getgrgid.c create mode 100644 nss/getgrgid_r.c create mode 100644 nss/getgrnam.c create mode 100644 nss/getgrnam_r.c create mode 100644 nss/grp-merge.c create mode 100644 nss/grp-merge.h create mode 100644 nss/grp.h create mode 100644 nss/initgroups-fallback.c create mode 100644 nss/initgroups.c create mode 100644 nss/putgrent.c create mode 100644 nss/testgrp.c create mode 100644 nss/tst-initgroups1.c create mode 100644 nss/tst-initgroups1.root/etc/group create mode 100644 nss/tst-initgroups1.root/etc/nsswitch.conf create mode 100644 nss/tst-initgroups1.root/etc/passwd create mode 100644 nss/tst-initgroups2.c create mode 100644 nss/tst-initgroups2.root/etc/group create mode 100644 nss/tst-initgroups2.root/etc/nsswitch.conf create mode 100644 nss/tst-initgroups2.root/etc/passwd create mode 100644 nss/tst-putgrent.c create mode 100644 nss/tst_fgetgrent.c create mode 100644 nss/tst_fgetgrent.sh (limited to 'nss') diff --git a/nss/Makefile b/nss/Makefile index 32764b74c0..baf7d9d0ab 100644 --- a/nss/Makefile +++ b/nss/Makefile @@ -23,6 +23,7 @@ subdir := nss include ../Makeconfig headers := \ + grp.h \ nss.h \ # headers @@ -50,6 +51,34 @@ routines = \ valid_list_field \ # routines +# grp routines: +routines += \ + fgetgrent \ + fgetgrent_r \ + getgrent \ + getgrent_r \ + getgrgid \ + getgrgid_r \ + getgrnam \ + getgrnam_r \ + grp-merge \ + initgroups \ + putgrent \ + # routines + +ifeq ($(have-thread-library),yes) +CFLAGS-fgetgrent.c += -fexceptions +CFLAGS-fgetgrent_r.c += -fexceptions $(libio-mtsafe) +CFLAGS-getgrent.c += -fexceptions +CFLAGS-getgrent_r.c += -fexceptions +CFLAGS-getgrgid.c += -fexceptions +CFLAGS-getgrgid_r.c += -fexceptions +CFLAGS-getgrnam.c += -fexceptions +CFLAGS-getgrnam_r.c += -fexceptions +CFLAGS-initgroups.c += -fexceptions +CFLAGS-putgrent.c += -fexceptions $(libio-mtsafe) +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. @@ -88,6 +117,7 @@ tests := \ bug17079 \ test-digits-dots \ test-netdb \ + testgrp \ tst-nss-getpwent \ tst-nss-hash \ tst-nss-test1 \ @@ -95,11 +125,14 @@ tests := \ tst-nss-test4 \ tst-nss-test5 \ tst-nss-test_errno \ + tst-putgrent \ # tests xtests = bug-erange tests-container := \ + tst-initgroups1 \ + tst-initgroups2 \ tst-nss-compat1 \ tst-nss-db-endgrent \ tst-nss-db-endpwent \ @@ -112,13 +145,21 @@ tests-container := \ tst-reload2 \ # tests-container -# Tests which need libdl ifeq (yes,$(build-shared)) +# Tests which need libdl tests += tst-nss-files-hosts-erange tests += tst-nss-files-hosts-multi tests += tst-nss-files-hosts-getent tests += tst-nss-files-alias-leak tests += tst-nss-files-alias-truncated +# tst_fgetgrent currently only works with shared libraries +test-srcs := tst_fgetgrent +ifeq ($(run-built-tests),yes) +tests-special += $(objpfx)tst_fgetgrent.out +$(objpfx)tst_fgetgrent.out: tst_fgetgrent.sh $(objpfx)tst_fgetgrent + $(SHELL) $< $(common-objpfx) '$(test-program-prefix)'; \ + $(evaluate-test) +endif endif # If we have a thread library then we can test cancellation against diff --git a/nss/Versions b/nss/Versions index e551524aa9..5401829911 100644 --- a/nss/Versions +++ b/nss/Versions @@ -5,10 +5,38 @@ libc { # Functions exported as no-op compat symbols. __nss_passwd_lookup; __nss_group_lookup; __nss_hosts_lookup; __nss_next; __nss_database_lookup; + + # e* + endgrent; + + # f* + fgetgrent; fgetgrent_r; + + # g* + getgrent; getgrent_r; getgrgid; getgrgid_r; getgrnam; getgrnam_r; + getgroups; + + # i* + initgroups; + + # s* + setgrent; + } + GLIBC_2.1 { + # p* + putgrent; + } + GLIBC_2.1.2 { + # g* + getgrent_r; getgrgid_r; getgrnam_r; } GLIBC_2.2.2 { __nss_hostname_digits_dots; } + GLIBC_2.2.4 { + # g* + getgrouplist; + } GLIBC_2.27 { } GLIBC_PRIVATE { @@ -107,6 +135,8 @@ libc { _nss_files_initgroups_dyn; _nss_files_init; + + __merge_grp; __copy_grp; } } diff --git a/nss/fgetgrent.c b/nss/fgetgrent.c new file mode 100644 index 0000000000..2e7c7fe5fb --- /dev/null +++ b/nss/fgetgrent.c @@ -0,0 +1,87 @@ +/* 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 + . */ + +#include +#include +#include +#include +#include +#include + + +/* We need to protect the dynamic buffer handling. */ +__libc_lock_define_initialized (static, lock); + +static char *buffer; + +/* Read one entry from the given stream. */ +struct group * +fgetgrent (FILE *stream) +{ + static size_t buffer_size; + static struct group resbuf; + fpos_t pos; + struct group *result; + int save; + + if (__builtin_expect (fgetpos (stream, &pos), 0) != 0) + return NULL; + + /* Get lock. */ + __libc_lock_lock (lock); + + /* Allocate buffer if not yet available. */ + if (buffer == NULL) + { + buffer_size = NSS_BUFLEN_GROUP; + buffer = malloc (buffer_size); + } + + while (buffer != NULL + && (__fgetgrent_r (stream, &resbuf, buffer, buffer_size, &result) + == ERANGE)) + { + char *new_buf; + buffer_size += NSS_BUFLEN_GROUP; + new_buf = realloc (buffer, buffer_size); + if (__glibc_unlikely (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_fgetgrent_freemem_ptr) diff --git a/nss/fgetgrent_r.c b/nss/fgetgrent_r.c new file mode 100644 index 0000000000..5ce70230f1 --- /dev/null +++ b/nss/fgetgrent_r.c @@ -0,0 +1,65 @@ +/* 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 + . */ + +#include +#include +#include +#include + +/* Define a line parsing function using the common code + used in the nss_files module. */ + +#define STRUCTURE group +#define ENTNAME grent +struct grent_data {}; + +#define TRAILING_LIST_MEMBER gr_mem +#define TRAILING_LIST_SEPARATOR_P(c) ((c) == ',') +#include +LINE_PARSER +(, + STRING_FIELD (result->gr_name, ISCOLON, 0); + if (line[0] == '\0' + && (result->gr_name[0] == '+' || result->gr_name[0] == '-')) + { + result->gr_passwd = NULL; + result->gr_gid = 0; + } + else + { + STRING_FIELD (result->gr_passwd, ISCOLON, 0); + if (result->gr_name[0] == '+' || result->gr_name[0] == '-') + INT_FIELD_MAYBE_NULL (result->gr_gid, ISCOLON, 0, 10, , 0) + else + INT_FIELD (result->gr_gid, ISCOLON, 0, 10,) + } + ) + + +/* Read one entry from the given stream. */ +int +__fgetgrent_r (FILE *stream, struct group *resbuf, char *buffer, size_t buflen, + struct group **result) +{ + int ret = __nss_fgetent_r (stream, resbuf, buffer, buflen, parse_line); + if (ret == 0) + *result = resbuf; + else + *result = NULL; + return ret; +} +weak_alias (__fgetgrent_r, fgetgrent_r) diff --git a/nss/getgrent.c b/nss/getgrent.c new file mode 100644 index 0000000000..6e09987318 --- /dev/null +++ b/nss/getgrent.c @@ -0,0 +1,28 @@ +/* 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 + . */ + +#include + + +#define LOOKUP_TYPE struct group +#define SETFUNC_NAME setgrent +#define GETFUNC_NAME getgrent +#define ENDFUNC_NAME endgrent +#define DATABASE_NAME group +#define BUFLEN NSS_BUFLEN_GROUP + +#include "../nss/getXXent.c" diff --git a/nss/getgrent_r.c b/nss/getgrent_r.c new file mode 100644 index 0000000000..ea3d0e89c1 --- /dev/null +++ b/nss/getgrent_r.c @@ -0,0 +1,28 @@ +/* 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 + . */ + +#include + + +#define LOOKUP_TYPE struct group +#define SETFUNC_NAME setgrent +#define GETFUNC_NAME getgrent +#define ENDFUNC_NAME endgrent +#define DATABASE_NAME group +#define BUFLEN NSS_BUFLEN_GROUP + +#include "../nss/getXXent_r.c" diff --git a/nss/getgrgid.c b/nss/getgrgid.c new file mode 100644 index 0000000000..db50f8fdac --- /dev/null +++ b/nss/getgrgid.c @@ -0,0 +1,28 @@ +/* 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 + . */ + +#include + + +#define LOOKUP_TYPE struct group +#define FUNCTION_NAME getgrgid +#define DATABASE_NAME group +#define ADD_PARAMS gid_t gid +#define ADD_VARIABLES gid +#define BUFLEN NSS_BUFLEN_GROUP + +#include "../nss/getXXbyYY.c" diff --git a/nss/getgrgid_r.c b/nss/getgrgid_r.c new file mode 100644 index 0000000000..dab5f85ef3 --- /dev/null +++ b/nss/getgrgid_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 + . */ + +#include + +#include + +#define LOOKUP_TYPE struct group +#define FUNCTION_NAME getgrgid +#define DATABASE_NAME group +#define ADD_PARAMS gid_t gid +#define ADD_VARIABLES gid +#define BUFLEN NSS_BUFLEN_GROUP +#define DEEPCOPY_FN __copy_grp +#define MERGE_FN __merge_grp + +#include diff --git a/nss/getgrnam.c b/nss/getgrnam.c new file mode 100644 index 0000000000..98d637b2bc --- /dev/null +++ b/nss/getgrnam.c @@ -0,0 +1,28 @@ +/* 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 + . */ + +#include + + +#define LOOKUP_TYPE struct group +#define FUNCTION_NAME getgrnam +#define DATABASE_NAME group +#define ADD_PARAMS const char *name +#define ADD_VARIABLES name +#define BUFLEN NSS_BUFLEN_GROUP + +#include "../nss/getXXbyYY.c" diff --git a/nss/getgrnam_r.c b/nss/getgrnam_r.c new file mode 100644 index 0000000000..ed5649c8d8 --- /dev/null +++ b/nss/getgrnam_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 + . */ + +#include + +#include + +#define LOOKUP_TYPE struct group +#define FUNCTION_NAME getgrnam +#define DATABASE_NAME group +#define ADD_PARAMS const char *name +#define ADD_VARIABLES name + +#define DEEPCOPY_FN __copy_grp +#define MERGE_FN __merge_grp + +#include diff --git a/nss/grp-merge.c b/nss/grp-merge.c new file mode 100644 index 0000000000..991abf0252 --- /dev/null +++ b/nss/grp-merge.c @@ -0,0 +1,200 @@ +/* Group merging implementation. + Copyright (C) 2016-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 + . */ + +#include +#include +#include +#include +#include + +#define BUFCHECK(size) \ + ({ \ + do \ + { \ + if (c + (size) > buflen) \ + { \ + free (members); \ + return ERANGE; \ + } \ + } \ + while (0); \ + }) + +int +__copy_grp (const struct group srcgrp, const size_t buflen, + struct group *destgrp, char *destbuf, char **endptr) +{ + size_t i; + size_t c = 0; + size_t len; + size_t memcount; + char **members = NULL; + + /* Copy the GID. */ + destgrp->gr_gid = srcgrp.gr_gid; + + /* Copy the name. */ + len = strlen (srcgrp.gr_name) + 1; + BUFCHECK (len); + memcpy (&destbuf[c], srcgrp.gr_name, len); + destgrp->gr_name = &destbuf[c]; + c += len; + + /* Copy the password. */ + len = strlen (srcgrp.gr_passwd) + 1; + BUFCHECK (len); + memcpy (&destbuf[c], srcgrp.gr_passwd, len); + destgrp->gr_passwd = &destbuf[c]; + c += len; + + /* Count all of the members. */ + for (memcount = 0; srcgrp.gr_mem[memcount]; memcount++) + ; + + /* Allocate a temporary holding area for the pointers to the member + contents, including space for a NULL-terminator. */ + members = malloc (sizeof (char *) * (memcount + 1)); + if (members == NULL) + return ENOMEM; + + /* Copy all of the group members to destbuf and add a pointer to each of + them into the 'members' array. */ + for (i = 0; srcgrp.gr_mem[i]; i++) + { + len = strlen (srcgrp.gr_mem[i]) + 1; + BUFCHECK (len); + memcpy (&destbuf[c], srcgrp.gr_mem[i], len); + members[i] = &destbuf[c]; + c += len; + } + members[i] = NULL; + + /* Align for pointers. We can't simply align C because we need to + align destbuf[c]. */ + if ((((uintptr_t)destbuf + c) & (__alignof__(char **) - 1)) != 0) + { + uintptr_t mis_align = ((uintptr_t)destbuf + c) & (__alignof__(char **) - 1); + c += __alignof__(char **) - mis_align; + } + + /* Copy the pointers from the members array into the buffer and assign them + to the gr_mem member of destgrp. */ + destgrp->gr_mem = (char **) &destbuf[c]; + len = sizeof (char *) * (memcount + 1); + BUFCHECK (len); + memcpy (&destbuf[c], members, len); + c += len; + free (members); + members = NULL; + + /* Save the count of members at the end. */ + BUFCHECK (sizeof (size_t)); + memcpy (&destbuf[c], &memcount, sizeof (size_t)); + c += sizeof (size_t); + + if (endptr) + *endptr = destbuf + c; + return 0; +} +libc_hidden_def (__copy_grp) + +/* Check that the name, GID and passwd fields match, then + copy in the gr_mem array. */ +int +__merge_grp (struct group *savedgrp, char *savedbuf, char *savedend, + size_t buflen, struct group *mergegrp, char *mergebuf) +{ + size_t c, i, len; + size_t savedmemcount; + size_t memcount; + size_t membersize; + char **members = NULL; + + /* We only support merging members of groups with identical names and + GID values. If we hit this case, we need to overwrite the current + buffer with the saved one (which is functionally equivalent to + treating the new lookup as NSS_STATUS_NOTFOUND). */ + if (mergegrp->gr_gid != savedgrp->gr_gid + || strcmp (mergegrp->gr_name, savedgrp->gr_name)) + return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL); + + /* Get the count of group members from the last sizeof (size_t) bytes in the + mergegrp buffer. */ + savedmemcount = *(size_t *) (savedend - sizeof (size_t)); + + /* Get the count of new members to add. */ + for (memcount = 0; mergegrp->gr_mem[memcount]; memcount++) + ; + + /* Create a temporary array to hold the pointers to the member values from + both the saved and merge groups. */ + membersize = savedmemcount + memcount + 1; + members = malloc (sizeof (char *) * membersize); + if (members == NULL) + return ENOMEM; + + /* Copy in the existing member pointers from the saved group + Note: this is not NULL-terminated yet. */ + memcpy (members, savedgrp->gr_mem, sizeof (char *) * savedmemcount); + + /* Back up into the savedbuf until we get back to the NULL-terminator of the + group member list. (This means walking back savedmemcount + 1 (char *) pointers + and the member count value. + The value of c is going to be the used length of the buffer backed up by + the member count and further backed up by the size of the pointers. */ + c = savedend - savedbuf + - sizeof (size_t) + - sizeof (char *) * (savedmemcount + 1); + + /* Add all the new group members, overwriting the old NULL-terminator while + adding the new pointers to the temporary array. */ + for (i = 0; mergegrp->gr_mem[i]; i++) + { + len = strlen (mergegrp->gr_mem[i]) + 1; + BUFCHECK (len); + memcpy (&savedbuf[c], mergegrp->gr_mem[i], len); + members[savedmemcount + i] = &savedbuf[c]; + c += len; + } + /* Add the NULL-terminator. */ + members[savedmemcount + memcount] = NULL; + + /* Align for pointers. We can't simply align C because we need to + align savedbuf[c]. */ + if ((((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1)) != 0) + { + uintptr_t mis_align = ((uintptr_t)savedbuf + c) & (__alignof__(char **) - 1); + c += __alignof__(char **) - mis_align; + } + + /* Copy the member array back into the buffer after the member list and free + the member array. */ + savedgrp->gr_mem = (char **) &savedbuf[c]; + len = sizeof (char *) * membersize; + BUFCHECK (len); + memcpy (&savedbuf[c], members, len); + c += len; + + free (members); + members = NULL; + + /* Finally, copy the results back into mergebuf, since that's the buffer + that we were provided by the caller. */ + return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL); +} +libc_hidden_def (__merge_grp) diff --git a/nss/grp-merge.h b/nss/grp-merge.h new file mode 100644 index 0000000000..9e1f75cfc7 --- /dev/null +++ b/nss/grp-merge.h @@ -0,0 +1,35 @@ +/* Group merging implementation. + Copyright (C) 2016-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 + . */ + +#ifndef _GRP_MERGE_H +#define _GRP_MERGE_H 1 + +#include + +/* Duplicate a grp struct (and its members). When no longer needed, the + calling function must free(newbuf). */ +int +__copy_grp (const struct group srcgrp, const size_t buflen, + struct group *destgrp, char *destbuf, char **endptr); + +/* Merge the member lists of two grp structs together. */ +int +__merge_grp (struct group *savedgrp, char *savedbuf, char *savedend, + size_t buflen, struct group *mergegrp, char *mergebuf); + +#endif /* _GRP_MERGE_H */ diff --git a/nss/grp.h b/nss/grp.h new file mode 100644 index 0000000000..c88964797c --- /dev/null +++ b/nss/grp.h @@ -0,0 +1,207 @@ +/* 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 + . */ + +/* + * POSIX Standard: 9.2.1 Group Database Access + */ + +#ifndef _GRP_H +#define _GRP_H 1 + +#include + +__BEGIN_DECLS + +#include + +#define __need_size_t +#include + + +/* For the Single Unix specification we must define this type here. */ +#if (defined __USE_XOPEN || defined __USE_XOPEN2K) && !defined __gid_t_defined +typedef __gid_t gid_t; +# define __gid_t_defined +#endif + +/* The group structure. */ +struct group + { + char *gr_name; /* Group name. */ + char *gr_passwd; /* Password. */ + __gid_t gr_gid; /* Group ID. */ + char **gr_mem; /* Member list. */ + }; + + +#ifdef __USE_MISC +# include +#endif + + +#if defined __USE_MISC || defined __USE_XOPEN_EXTENDED +/* Rewind the group-file stream. + + This function is a possible cancellation point and therefore not + marked with __THROW. */ +extern void setgrent (void); + +/* Close the group-file stream. + + This function is a possible cancellation point and therefore not + marked with __THROW. */ +extern void endgrent (void); + +/* Read an entry from the group-file stream, opening it if necessary. + + This function is a possible cancellation point and therefore not + marked with __THROW. */ +extern struct group *getgrent (void); +#endif + +#ifdef __USE_MISC +/* Read a group 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 group *fgetgrent (FILE *__stream); +#endif + +#ifdef __USE_GNU +/* Write the given entry onto the given 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 putgrent (const struct group *__restrict __p, + FILE *__restrict __f); +#endif + +/* Search for an entry with a matching group ID. + + This function is a possible cancellation point and therefore not + marked with __THROW. */ +extern struct group *getgrgid (__gid_t __gid); + +/* Search for an entry with a matching group name. + + This function is a possible cancellation point and therefore not + marked with __THROW. */ +extern struct group *getgrnam (const char *__name); + +#ifdef __USE_POSIX + +# ifdef __USE_MISC +/* Reasonable value for the buffer sized used in the reentrant + functions below. But better use `sysconf'. */ +# define NSS_BUFLEN_GROUP 1024 +# endif + +/* Reentrant versions of some of the functions above. + + PLEASE NOTE: the `getgrent_r' function is not (yet) standardized. + The interface may change in later versions of this library. But + the interface is designed following the principals used for the + other reentrant functions so the chances are good this is what the + POSIX people would choose. + + 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. */ + +# ifdef __USE_GNU +extern int getgrent_r (struct group *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct group **__restrict __result) + __attr_access ((__write_only__, 2, 3)); +# endif + +/* Search for an entry with a matching group ID. + + This function is a possible cancellation point and therefore not + marked with __THROW. */ +extern int getgrgid_r (__gid_t __gid, struct group *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct group **__restrict __result) + __attr_access ((__write_only__, 3, 4)); + +/* Search for an entry with a matching group name. + + This function is a possible cancellation point and therefore not + marked with __THROW. */ +extern int getgrnam_r (const char *__restrict __name, + struct group *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct group **__restrict __result) + __attr_access ((__write_only__, 3, 4)); + +# ifdef __USE_MISC +/* Read a group entry from STREAM. This function is not standardized + an probably never will. + + 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 fgetgrent_r (FILE *__restrict __stream, + struct group *__restrict __resultbuf, + char *__restrict __buffer, size_t __buflen, + struct group **__restrict __result) + __attr_access ((__write_only__, 3, 4)); +# endif + +#endif /* POSIX or reentrant */ + + +#ifdef __USE_MISC + +# define __need_size_t +# include + +/* Set the group set for the current user to GROUPS (N of them). */ +extern int setgroups (size_t __n, const __gid_t *__groups) __THROW; + +/* Store at most *NGROUPS members of the group set for USER into + *GROUPS. Also include GROUP. The actual number of groups found is + returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. + + 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 getgrouplist (const char *__user, __gid_t __group, + __gid_t *__groups, int *__ngroups); + +/* Initialize the group set for the current user + by reading the group database and using all groups + of which USER is a member. Also include GROUP. + + 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 initgroups (const char *__user, __gid_t __group); + +#endif /* Use misc. */ + +__END_DECLS + +#endif /* grp.h */ diff --git a/nss/initgroups-fallback.c b/nss/initgroups-fallback.c new file mode 100644 index 0000000000..9df940767b --- /dev/null +++ b/nss/initgroups-fallback.c @@ -0,0 +1,116 @@ +/* Prototype for the setgrent functions we use here. */ +typedef enum nss_status (*set_function) (void); + +/* Prototype for the endgrent functions we use here. */ +typedef enum nss_status (*end_function) (void); + +/* Prototype for the setgrent functions we use here. */ +typedef enum nss_status (*get_function) (struct group *, char *, + size_t, int *); + + +static enum nss_status +compat_call (nss_action_list nip, const char *user, gid_t group, long int *start, + long int *size, gid_t **groupsp, long int limit, int *errnop) +{ + struct group grpbuf; + enum nss_status status; + set_function setgrent_fct; + get_function getgrent_fct; + end_function endgrent_fct; + gid_t *groups = *groupsp; + + getgrent_fct = __nss_lookup_function (nip, "getgrent_r"); + if (getgrent_fct == NULL) + return NSS_STATUS_UNAVAIL; + + setgrent_fct = __nss_lookup_function (nip, "setgrent"); + if (setgrent_fct) + { + status = DL_CALL_FCT (setgrent_fct, ()); + if (status != NSS_STATUS_SUCCESS) + return status; + } + + endgrent_fct = __nss_lookup_function (nip, "endgrent"); + + struct scratch_buffer tmpbuf; + scratch_buffer_init (&tmpbuf); + enum nss_status result = NSS_STATUS_SUCCESS; + + do + { + while ((status = DL_CALL_FCT (getgrent_fct, + (&grpbuf, tmpbuf.data, tmpbuf.length, + errnop)), + status == NSS_STATUS_TRYAGAIN) + && *errnop == ERANGE) + { + if (!scratch_buffer_grow (&tmpbuf)) + { + result = NSS_STATUS_TRYAGAIN; + goto done; + } + } + + if (status != NSS_STATUS_SUCCESS) + goto done; + + if (grpbuf.gr_gid != group) + { + char **m; + + for (m = grpbuf.gr_mem; *m != NULL; ++m) + if (strcmp (*m, user) == 0) + { + /* Check whether the group is already on the list. */ + long int cnt; + for (cnt = 0; cnt < *start; ++cnt) + if (groups[cnt] == grpbuf.gr_gid) + break; + + if (cnt == *start) + { + /* Matches user and not yet on the list. Insert + this group. */ + if (__glibc_unlikely (*start == *size)) + { + /* Need a bigger buffer. */ + gid_t *newgroups; + long int newsize; + + if (limit > 0 && *size == limit) + /* We reached the maximum. */ + goto done; + + if (limit <= 0) + newsize = 2 * *size; + else + newsize = MIN (limit, 2 * *size); + + newgroups = realloc (groups, + newsize * sizeof (*groups)); + if (newgroups == NULL) + goto done; + *groupsp = groups = newgroups; + *size = newsize; + } + + groups[*start] = grpbuf.gr_gid; + *start += 1; + } + + break; + } + } + } + while (status == NSS_STATUS_SUCCESS); + + done: + scratch_buffer_free (&tmpbuf); + + if (endgrent_fct) + DL_CALL_FCT (endgrent_fct, ()); + + return result; +} diff --git a/nss/initgroups.c b/nss/initgroups.c new file mode 100644 index 0000000000..e803cecebc --- /dev/null +++ b/nss/initgroups.c @@ -0,0 +1,218 @@ +/* Copyright (C) 1989, 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "../nscd/nscd-client.h" +#include "../nscd/nscd_proto.h" + +/* Type of the lookup function. */ +typedef enum nss_status (*initgroups_dyn_function) (const char *, gid_t, + long int *, long int *, + gid_t **, long int, int *); + +static bool use_initgroups_entry; + + +#include "initgroups-fallback.c" + + +static int +internal_getgrouplist (const char *user, gid_t group, long int *size, + gid_t **groupsp, long int limit) +{ +#ifdef USE_NSCD + if (__nss_not_use_nscd_group > 0 + && ++__nss_not_use_nscd_group > NSS_NSCD_RETRY) + __nss_not_use_nscd_group = 0; + if (!__nss_not_use_nscd_group + && !__nss_database_custom[NSS_DBSIDX_group]) + { + int n = __nscd_getgrouplist (user, group, size, groupsp, limit); + if (n >= 0) + return n; + + /* nscd is not usable. */ + __nss_not_use_nscd_group = 1; + } +#endif + + enum nss_status status = NSS_STATUS_UNAVAIL; + + /* Never store more than the starting *SIZE number of elements. */ + assert (*size > 0); + (*groupsp)[0] = group; + /* Start is one, because we have the first group as parameter. */ + long int start = 1; + + nss_action_list nip; + + if (__nss_database_get (nss_database_initgroups, &nip) + && nip != NULL) + { + use_initgroups_entry = true; + } + else if (__nss_database_get (nss_database_group, &nip) + && nip != NULL) + { + use_initgroups_entry = false; + } + else + { + nip = __nss_action_parse ("files"); + use_initgroups_entry = false; + } + + while (nip && nip->module) + { + long int prev_start = start; + + initgroups_dyn_function fct = __nss_lookup_function (nip, + "initgroups_dyn"); + if (fct == NULL) + status = compat_call (nip, user, group, &start, size, groupsp, + limit, &errno); + else + status = DL_CALL_FCT (fct, (user, group, &start, size, groupsp, + limit, &errno)); + + /* Remove duplicates. */ + long int cnt = prev_start; + while (cnt < start) + { + long int inner; + for (inner = 0; inner < prev_start; ++inner) + if ((*groupsp)[inner] == (*groupsp)[cnt]) + break; + + if (inner < prev_start) + (*groupsp)[cnt] = (*groupsp)[--start]; + else + ++cnt; + } + + /* This is really only for debugging. */ + if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN) + __libc_fatal ("Illegal status in internal_getgrouplist.\n"); + + /* For compatibility reason we will continue to look for more + entries using the next service even though data has already + been found if the nsswitch.conf file contained only a 'groups' + line and no 'initgroups' line. If the latter is available + we always respect the status. This means that the default + for successful lookups is to return. */ + if ((use_initgroups_entry || status != NSS_STATUS_SUCCESS) + && nss_next_action (nip, status) == NSS_ACTION_RETURN) + break; + + nip++; + } + + return start; +} + +/* Store at most *NGROUPS members of the group set for USER into + *GROUPS. Also include GROUP. The actual number of groups found is + returned in *NGROUPS. Return -1 if the if *NGROUPS is too small. */ +int +getgrouplist (const char *user, gid_t group, gid_t *groups, int *ngroups) +{ + long int size = MAX (1, *ngroups); + + gid_t *newgroups = (gid_t *) malloc (size * sizeof (gid_t)); + if (__glibc_unlikely (newgroups == NULL)) + /* No more memory. */ + // XXX This is wrong. The user provided memory, we have to use + // XXX it. The internal functions must be called with the user + // XXX provided buffer and not try to increase the size if it is + // XXX too small. For initgroups a flag could say: increase size. + return -1; + + int total = internal_getgrouplist (user, group, &size, &newgroups, -1); + + memcpy (groups, newgroups, MIN (*ngroups, total) * sizeof (gid_t)); + + free (newgroups); + + int retval = total > *ngroups ? -1 : total; + *ngroups = total; + + return retval; +} + +nss_interface_function (getgrouplist) + +/* Initialize the group set for the current user + by reading the group database and using all groups + of which USER is a member. Also include GROUP. */ +int +initgroups (const char *user, gid_t group) +{ +#if defined NGROUPS_MAX && NGROUPS_MAX == 0 + + /* No extra groups allowed. */ + return 0; + +#else + + long int size; + gid_t *groups; + int ngroups; + int result; + + /* We always use sysconf even if NGROUPS_MAX is defined. That way, the + limit can be raised in the kernel configuration without having to + recompile libc. */ + long int limit = __sysconf (_SC_NGROUPS_MAX); + + if (limit > 0) + /* We limit the size of the initially allocated array. */ + size = MIN (limit, 64); + else + /* No fixed limit on groups. Pick a starting buffer size. */ + size = 16; + + groups = (gid_t *) malloc (size * sizeof (gid_t)); + if (__glibc_unlikely (groups == NULL)) + /* No more memory. */ + return -1; + + ngroups = internal_getgrouplist (user, group, &size, &groups, limit); + + /* Try to set the maximum number of groups the kernel can handle. */ + do + result = setgroups (ngroups, groups); + while (result == -1 && errno == EINVAL && --ngroups > 0); + + free (groups); + + return result; +#endif +} + +nss_interface_function (initgroups) diff --git a/nss/putgrent.c b/nss/putgrent.c new file mode 100644 index 0000000000..93caea5071 --- /dev/null +++ b/nss/putgrent.c @@ -0,0 +1,76 @@ +/* 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 + . */ + +#include +#include +#include +#include +#include + +#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 group file. */ +int +putgrent (const struct group *gr, FILE *stream) +{ + int retval; + + if (__glibc_unlikely (gr == NULL) || __glibc_unlikely (stream == NULL) + || gr->gr_name == NULL || !__nss_valid_field (gr->gr_name) + || !__nss_valid_field (gr->gr_passwd) + || !__nss_valid_list_field (gr->gr_mem)) + { + __set_errno (EINVAL); + return -1; + } + + flockfile (stream); + + if (gr->gr_name[0] == '+' || gr->gr_name[0] == '-') + retval = fprintf (stream, "%s:%s::", + gr->gr_name, _S (gr->gr_passwd)); + else + retval = fprintf (stream, "%s:%s:%lu:", + gr->gr_name, _S (gr->gr_passwd), + (unsigned long int) gr->gr_gid); + if (__builtin_expect (retval, 0) < 0) + { + funlockfile (stream); + return -1; + } + + if (gr->gr_mem != NULL) + { + for (size_t i = 0; gr->gr_mem[i] != NULL; i++) + if (fprintf (stream, i == 0 ? "%s" : ",%s", gr->gr_mem[i]) < 0) + { + /* What else can we do? */ + funlockfile (stream); + return -1; + } + } + + retval = fputc_unlocked ('\n', stream); + + funlockfile (stream); + + return retval < 0 ? -1 : 0; +} diff --git a/nss/testgrp.c b/nss/testgrp.c new file mode 100644 index 0000000000..892cfaaa21 --- /dev/null +++ b/nss/testgrp.c @@ -0,0 +1,41 @@ +#include +#include +#include +#include +#include +#include + +int +main (int argc, char *argv[]) +{ + uid_t me; + struct passwd *my_passwd; + struct group *my_group = NULL; + char **members; + + me = getuid (); + my_passwd = getpwuid (me); + if (my_passwd == NULL) + printf ("Cannot find user entry for UID %d\n", me); + else + { + printf ("My login name is %s.\n", my_passwd->pw_name); + printf ("My uid is %d.\n", (int)(my_passwd->pw_uid)); + printf ("My home directory is %s.\n", my_passwd->pw_dir); + printf ("My default shell is %s.\n", my_passwd->pw_shell); + + my_group = getgrgid (my_passwd->pw_gid); + if (my_group == NULL) + printf ("No data for group %d found\n", my_passwd->pw_gid); + else + { + printf ("My default group is %s (%d).\n", + my_group->gr_name, (int)(my_passwd->pw_gid)); + printf ("The members of this group are:\n"); + for (members = my_group->gr_mem; *members != NULL; ++members) + printf (" %s\n", *members); + } + } + + return my_passwd && my_group ? EXIT_SUCCESS : EXIT_FAILURE; +} diff --git a/nss/tst-initgroups1.c b/nss/tst-initgroups1.c new file mode 100644 index 0000000000..3f1238875e --- /dev/null +++ b/nss/tst-initgroups1.c @@ -0,0 +1,56 @@ +/* Test that initgroups works. + 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 + . */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +/* Test that initgroups includes secondary groups. + https://bugzilla.redhat.com/show_bug.cgi?id=1906066 */ + +/* This version uses the wrapper around the groups module. */ + +#define EXPECTED_N_GROUPS 4 +static gid_t expected_groups[] = + { 20, 30, 50, 51 }; + +static int +do_test (void) +{ + gid_t mygroups [50]; + int i, n; + + n = 50; + getgrouplist ("dj", 20, mygroups, &n); + + TEST_COMPARE (n, EXPECTED_N_GROUPS); + for (i=0; i diff --git a/nss/tst-initgroups1.root/etc/group b/nss/tst-initgroups1.root/etc/group new file mode 100644 index 0000000000..0dac1cc2ba --- /dev/null +++ b/nss/tst-initgroups1.root/etc/group @@ -0,0 +1,7 @@ +abc:x:10: +def:x:20: +ghi:x:30:dj +jkl:x:40: +m:x:50:not,dj +n:x:51:dj,not +np:x:60:djx diff --git a/nss/tst-initgroups1.root/etc/nsswitch.conf b/nss/tst-initgroups1.root/etc/nsswitch.conf new file mode 100644 index 0000000000..8d0a1aea13 --- /dev/null +++ b/nss/tst-initgroups1.root/etc/nsswitch.conf @@ -0,0 +1 @@ +group : files diff --git a/nss/tst-initgroups1.root/etc/passwd b/nss/tst-initgroups1.root/etc/passwd new file mode 100644 index 0000000000..5e3a2a5eea --- /dev/null +++ b/nss/tst-initgroups1.root/etc/passwd @@ -0,0 +1 @@ +dj:x:84:20:DJ:/:/bin/sh diff --git a/nss/tst-initgroups2.c b/nss/tst-initgroups2.c new file mode 100644 index 0000000000..4e8b8c5cd0 --- /dev/null +++ b/nss/tst-initgroups2.c @@ -0,0 +1,21 @@ +/* Test that initgroups works. + 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 + . */ + +#include "tst-initgroups1.c" + +/* This version uses the initgroups built in to the files module. */ diff --git a/nss/tst-initgroups2.root/etc/group b/nss/tst-initgroups2.root/etc/group new file mode 100644 index 0000000000..0dac1cc2ba --- /dev/null +++ b/nss/tst-initgroups2.root/etc/group @@ -0,0 +1,7 @@ +abc:x:10: +def:x:20: +ghi:x:30:dj +jkl:x:40: +m:x:50:not,dj +n:x:51:dj,not +np:x:60:djx diff --git a/nss/tst-initgroups2.root/etc/nsswitch.conf b/nss/tst-initgroups2.root/etc/nsswitch.conf new file mode 100644 index 0000000000..c61f3624f6 --- /dev/null +++ b/nss/tst-initgroups2.root/etc/nsswitch.conf @@ -0,0 +1,2 @@ +initgroups : files +group : notfiles diff --git a/nss/tst-initgroups2.root/etc/passwd b/nss/tst-initgroups2.root/etc/passwd new file mode 100644 index 0000000000..5e3a2a5eea --- /dev/null +++ b/nss/tst-initgroups2.root/etc/passwd @@ -0,0 +1 @@ +dj:x:84:20:DJ:/:/bin/sh diff --git a/nss/tst-putgrent.c b/nss/tst-putgrent.c new file mode 100644 index 0000000000..79c14862bd --- /dev/null +++ b/nss/tst-putgrent.c @@ -0,0 +1,167 @@ +/* Test for processing of invalid group 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 + . */ + +#include +#include +#include +#include +#include +#include + +static bool errors; + +static void +check (struct group 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 = putgrent (&e, f); + + if (expected == NULL) + { + if (ret == -1) + { + if (errno != EINVAL) + { + printf ("putgrent: unexpected error code: %m\n"); + errors = true; + } + } + else + { + printf ("putgrent: unexpected success (\"%s\", \"%s\")\n", + e.gr_name, e.gr_passwd); + 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) + { + buf[written - 1] = '\0'; + printf ("FAILED: \"%s\" (%ld), expected \"%s\" (%zu)\n", + buf, written - 1, expected, expected_length); + errors = true; + } + } + else + { + printf ("FAILED: putgrent (expected \"%s\"): %m\n", expected); + errors = true; + } + } + + fclose (f); + free (buf); +} + +static int +do_test (void) +{ + check ((struct group) { + .gr_name = (char *) "root", + }, + "root::0:"); + check ((struct group) { + .gr_name = (char *) "root", + .gr_passwd = (char *) "password", + .gr_gid = 1234, + .gr_mem = (char *[2]) {(char *) "member1", NULL} + }, + "root:password:1234:member1"); + check ((struct group) { + .gr_name = (char *) "root", + .gr_passwd = (char *) "password", + .gr_gid = 1234, + .gr_mem = (char *[3]) {(char *) "member1", (char *) "member2", NULL} + }, + "root:password:1234:member1,member2"); + + /* 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 group) { + .gr_name = (char *) *bad, + }, NULL); + check ((struct group) { + .gr_name = (char *) "root", + .gr_passwd = (char *) *bad, + }, NULL); + } + check ((struct group) { + .gr_name = (char *) "root", + .gr_passwd = (char *) "password", + .gr_mem = members, + }, NULL); + } + } + + return errors; +} + +#define TEST_FUNCTION do_test () +#include "../test-skeleton.c" diff --git a/nss/tst_fgetgrent.c b/nss/tst_fgetgrent.c new file mode 100644 index 0000000000..be41191824 --- /dev/null +++ b/nss/tst_fgetgrent.c @@ -0,0 +1,126 @@ +/* Copyright (C) 1999-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 + . */ + +#include +#include +#include +#include +#include +#include + +static int errors; + +static void +write_users (FILE *f, int large_pos, int pos) +{ + int i; + + if (pos == large_pos) + { + if (large_pos == 3) + fprintf (f, ":three"); + + /* we need more than 2048 bytes for proper testing. */ + for (i = 0; i < 500; i++) + fprintf (f, ",user%03d", i); + } + fprintf (f, "\n"); + +} + +static void +write_group (const char *filename, int pos) +{ + FILE *f; + + f = fopen (filename, "w"); + fprintf (f, "one:x:1:one"); + write_users (f, pos, 1); + fprintf (f, "two:x:2:two"); + write_users (f, pos, 2); + fprintf (f, "three:x:3"); + write_users (f, pos, 3); + fclose (f); +} + +static void +test_entry (const char *name, gid_t gid, struct group *g) +{ + if (!g) + { + printf ("Error: Entry is empty\n"); + errors++; + return; + } + + if ((g->gr_gid == gid) && (strcmp (g->gr_name, name) == 0)) + printf ("Ok: %s: %d\n", g->gr_name, g->gr_gid); + else + { + printf ("Error: %s: %d should be: %s: %d\n", g->gr_name, g->gr_gid, + name, gid); + errors++; + } +} + + +static void +test_fgetgrent (const char *filename) +{ + struct group *g; + FILE *f; + + f = fopen (filename,"r"); + + g = fgetgrent (f); + test_entry ("one", 1, g); + g = fgetgrent (f); + test_entry ("two", 2, g); + g = fgetgrent (f); + test_entry ("three", 3, g); + fclose (f); +} + + +int +main (int argc, char *argv[]) +{ + char file[] = "/tmp/tst_fgetgrent.XXXXXX"; + int fd = mkstemp (file); + if (fd == -1) + { + printf ("mkstemp failed: %m\n"); + return 1; + } + close (fd); + int i = 0; + + if (argc > 1) + i = atoi (argv[1]); + if (i > 3) + i = 3; + if (i) + printf ("Large group is group: %d\n", i); + else + printf ("Not using a large group\n"); + write_group (file, i); + test_fgetgrent (file); + + remove (file); + + return (errors != 0); +} diff --git a/nss/tst_fgetgrent.sh b/nss/tst_fgetgrent.sh new file mode 100644 index 0000000000..fb6b0c4179 --- /dev/null +++ b/nss/tst_fgetgrent.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Copyright (C) 1999-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 +# . + +set -e + +common_objpfx=$1; shift +test_program_prefix=$1; shift + +testout=${common_objpfx}/nss/tst_fgetgrent.out + +result=0 + +${test_program_prefix} \ +${common_objpfx}nss/tst_fgetgrent 0 > ${testout} || result=1 + +${test_program_prefix} \ +${common_objpfx}nss/tst_fgetgrent 1 >> ${testout} || result=1 + +${test_program_prefix} \ +${common_objpfx}nss/tst_fgetgrent 2 >> ${testout} || result=1 + +${test_program_prefix} \ +${common_objpfx}nss/tst_fgetgrent 3 >> ${testout} || result=1 + +exit $result -- cgit 1.4.1