diff --git a/glibc-testsuite.changes b/glibc-testsuite.changes index 24cddc2..fb3c08a 100644 --- a/glibc-testsuite.changes +++ b/glibc-testsuite.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Wed Oct 4 12:14:30 UTC 2017 - schwab@suse.de + +- nss-compat.patch: Move nss_compat from nis to nss subdir and install it + unconditionally +- nsswitch.conf: switch back to compat for passwd, group, shadow + ------------------------------------------------------------------- Thu Sep 28 07:57:52 UTC 2017 - schwab@suse.de diff --git a/glibc-testsuite.spec b/glibc-testsuite.spec index b2d600a..2722f7f 100644 --- a/glibc-testsuite.spec +++ b/glibc-testsuite.spec @@ -293,6 +293,8 @@ Patch1009: resolv-conf-oom.patch Patch1010: dynarray-allocation.patch # PATCH-FIX-UPSTREAM Avoid spurious inexact in nearbyint (BZ #22225) Patch1011: nearbyint-inexact.patch +# PATCH-FIX-UPSTREAM Move nss_compat from nis to nss subdir and install it unconditionally +Patch1012: nss-compat.patch ### # Patches awaiting upstream approval @@ -534,6 +536,7 @@ rm nscd/s-stamp %patch1009 -p1 %patch1010 -p1 %patch1011 -p1 +%patch1012 -p1 %patch2000 -p1 %patch2001 -p1 @@ -1215,6 +1218,8 @@ exit 0 %endif /%{_lib}/libnsl-%{libversion}.so /%{_lib}/libnsl.so.1 +/%{_lib}/libnss_compat-%{libversion}.so +/%{_lib}/libnss_compat.so.2 /%{_lib}/libnss_db-%{libversion}.so /%{_lib}/libnss_db.so.2 /%{_lib}/libnss_dns-%{libversion}.so diff --git a/glibc-utils.changes b/glibc-utils.changes index 24cddc2..fb3c08a 100644 --- a/glibc-utils.changes +++ b/glibc-utils.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Wed Oct 4 12:14:30 UTC 2017 - schwab@suse.de + +- nss-compat.patch: Move nss_compat from nis to nss subdir and install it + unconditionally +- nsswitch.conf: switch back to compat for passwd, group, shadow + ------------------------------------------------------------------- Thu Sep 28 07:57:52 UTC 2017 - schwab@suse.de diff --git a/glibc-utils.spec b/glibc-utils.spec index 0fdf13b..2a287c1 100644 --- a/glibc-utils.spec +++ b/glibc-utils.spec @@ -293,6 +293,8 @@ Patch1009: resolv-conf-oom.patch Patch1010: dynarray-allocation.patch # PATCH-FIX-UPSTREAM Avoid spurious inexact in nearbyint (BZ #22225) Patch1011: nearbyint-inexact.patch +# PATCH-FIX-UPSTREAM Move nss_compat from nis to nss subdir and install it unconditionally +Patch1012: nss-compat.patch ### # Patches awaiting upstream approval @@ -534,6 +536,7 @@ rm nscd/s-stamp %patch1009 -p1 %patch1010 -p1 %patch1011 -p1 +%patch1012 -p1 %patch2000 -p1 %patch2001 -p1 @@ -1215,6 +1218,8 @@ exit 0 %endif /%{_lib}/libnsl-%{libversion}.so /%{_lib}/libnsl.so.1 +/%{_lib}/libnss_compat-%{libversion}.so +/%{_lib}/libnss_compat.so.2 /%{_lib}/libnss_db-%{libversion}.so /%{_lib}/libnss_db.so.2 /%{_lib}/libnss_dns-%{libversion}.so diff --git a/glibc.changes b/glibc.changes index 24cddc2..fb3c08a 100644 --- a/glibc.changes +++ b/glibc.changes @@ -1,3 +1,10 @@ +------------------------------------------------------------------- +Wed Oct 4 12:14:30 UTC 2017 - schwab@suse.de + +- nss-compat.patch: Move nss_compat from nis to nss subdir and install it + unconditionally +- nsswitch.conf: switch back to compat for passwd, group, shadow + ------------------------------------------------------------------- Thu Sep 28 07:57:52 UTC 2017 - schwab@suse.de diff --git a/glibc.spec b/glibc.spec index 1988d7c..51c1ef3 100644 --- a/glibc.spec +++ b/glibc.spec @@ -299,6 +299,8 @@ Patch1009: resolv-conf-oom.patch Patch1010: dynarray-allocation.patch # PATCH-FIX-UPSTREAM Avoid spurious inexact in nearbyint (BZ #22225) Patch1011: nearbyint-inexact.patch +# PATCH-FIX-UPSTREAM Move nss_compat from nis to nss subdir and install it unconditionally +Patch1012: nss-compat.patch ### # Patches awaiting upstream approval @@ -540,6 +542,7 @@ rm nscd/s-stamp %patch1009 -p1 %patch1010 -p1 %patch1011 -p1 +%patch1012 -p1 %patch2000 -p1 %patch2001 -p1 @@ -1221,6 +1224,8 @@ exit 0 %endif /%{_lib}/libnsl-%{libversion}.so /%{_lib}/libnsl.so.1 +/%{_lib}/libnss_compat-%{libversion}.so +/%{_lib}/libnss_compat.so.2 /%{_lib}/libnss_db-%{libversion}.so /%{_lib}/libnss_db.so.2 /%{_lib}/libnss_dns-%{libversion}.so diff --git a/nss-compat.patch b/nss-compat.patch new file mode 100644 index 0000000..cc4819e --- /dev/null +++ b/nss-compat.patch @@ -0,0 +1,6745 @@ +2017-10-04 Andreas Schwab + + * nis/Makefile (services): Remove compat. + (libnss_compat-routines, libnss_compat-inhibit-o): Don't define. + ($(objpfx)libnss_compat.so): Remove rule. + * nis/Versions (libnss_compat): Remove. + * nss/Makefile (services): Add compat. + (libnss_compat-routines, libnss_compat-inhibit-o): Define. + * nss/Versions (libnss_compat): Define. + * nss/nss_compat/compat-grp.c: Moved here from nis/nss_compat. + Don't include . Replace bool_t by bool. + * nss/nss_compat/compat-initgroups.c: Likewise. + * nss/nss_compat/compat-pwd.c: Likewise. Include "nisdomain.h" + instead of . + (getpwent_next_nss_netgr): Use __nss_get_default_domain instead of + yp_get_default_domain. + * nss/nss_compat/compat-pwd.c: Likewise. + (getspent_next_nss_netgr): Use __nss_get_default_domain instead of + yp_get_default_domain. + * nss/nss_compat/nisdomain.c: New file. + * nss/nss_compat/nisdomain.h: Likewise. + +Index: glibc-2.26/nis/Makefile +=================================================================== +--- glibc-2.26.orig/nis/Makefile ++++ glibc-2.26/nis/Makefile +@@ -33,7 +33,7 @@ databases = proto service hosts network + spwd netgrp alias publickey + + # Specify rules for the nss_* modules. +-services := nis nisplus compat ++services := nis nisplus + endif + + extra-libs = libnsl +@@ -63,9 +63,6 @@ libnsl-routines = yp_xdr ypclnt ypupdate + nis_clone_res nss-default + + ifeq ($(build-obsolete-nsl),yes) +-libnss_compat-routines := $(addprefix compat-,grp pwd spwd initgroups) +-libnss_compat-inhibit-o = $(filter-out .os,$(object-suffixes)) +- + libnss_nis-routines := $(addprefix nis-,$(databases)) nis-initgroups \ + nss-nis + libnss_nis-inhibit-o = $(filter-out .os,$(object-suffixes)) +@@ -79,7 +76,6 @@ include ../Rules + + + ifeq ($(build-obsolete-nsl),yes) +-$(objpfx)libnss_compat.so: $(objpfx)libnsl.so$(libnsl.so-version) + $(objpfx)libnss_nis.so: $(objpfx)libnsl.so$(libnsl.so-version) \ + $(common-objpfx)nss/libnss_files.so + $(objpfx)libnss_nisplus.so: $(objpfx)libnsl.so$(libnsl.so-version) +Index: glibc-2.26/nis/Versions +=================================================================== +--- glibc-2.26.orig/nis/Versions ++++ glibc-2.26/nis/Versions +@@ -63,17 +63,6 @@ libnsl { + } + } + +-libnss_compat { +- GLIBC_PRIVATE { +- _nss_compat_endgrent; _nss_compat_endpwent; _nss_compat_endspent; +- _nss_compat_getgrent_r; _nss_compat_getgrgid_r; _nss_compat_getgrnam_r; +- _nss_compat_getpwent_r; _nss_compat_getpwnam_r; _nss_compat_getpwuid_r; +- _nss_compat_getspent_r; _nss_compat_getspnam_r; +- _nss_compat_setgrent; _nss_compat_setpwent; _nss_compat_setspent; +- _nss_compat_initgroups_dyn; +- } +-} +- + libnss_nis { + GLIBC_PRIVATE { + _nss_nis_endaliasent; _nss_nis_endetherent; _nss_nis_endgrent; +Index: glibc-2.26/nis/nss_compat/compat-grp.c +=================================================================== +--- glibc-2.26.orig/nis/nss_compat/compat-grp.c ++++ /dev/null +@@ -1,683 +0,0 @@ +-/* Copyright (C) 1996-2017 Free Software Foundation, Inc. +- This file is part of the GNU C Library. +- Contributed by Thorsten Kukuk , 1996. +- +- 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 +- +-static service_user *ni; +-static enum nss_status (*nss_setgrent) (int stayopen); +-static enum nss_status (*nss_getgrnam_r) (const char *name, +- struct group * grp, char *buffer, +- size_t buflen, int *errnop); +-static enum nss_status (*nss_getgrgid_r) (gid_t gid, struct group * grp, +- char *buffer, size_t buflen, +- int *errnop); +-static enum nss_status (*nss_getgrent_r) (struct group * grp, char *buffer, +- size_t buflen, int *errnop); +-static enum nss_status (*nss_endgrent) (void); +- +-/* Get the declaration of the parser function. */ +-#define ENTNAME grent +-#define STRUCTURE group +-#define EXTERN_PARSER +-#include +- +-/* Structure for remembering -group members ... */ +-#define BLACKLIST_INITIAL_SIZE 512 +-#define BLACKLIST_INCREMENT 256 +-struct blacklist_t +-{ +- char *data; +- int current; +- int size; +-}; +- +-struct ent_t +-{ +- bool_t files; +- enum nss_status setent_status; +- FILE *stream; +- struct blacklist_t blacklist; +-}; +-typedef struct ent_t ent_t; +- +-static ent_t ext_ent = { TRUE, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; +- +-/* Protect global state against multiple changers. */ +-__libc_lock_define_initialized (static, lock) +- +-/* Prototypes for local functions. */ +-static void blacklist_store_name (const char *, ent_t *); +-static int in_blacklist (const char *, int, ent_t *); +- +-/* Initialize the NSS interface/functions. The calling function must +- hold the lock. */ +-static void +-init_nss_interface (void) +-{ +- if (__nss_database_lookup ("group_compat", NULL, "nis", &ni) >= 0) +- { +- nss_setgrent = __nss_lookup_function (ni, "setgrent"); +- nss_getgrnam_r = __nss_lookup_function (ni, "getgrnam_r"); +- nss_getgrgid_r = __nss_lookup_function (ni, "getgrgid_r"); +- nss_getgrent_r = __nss_lookup_function (ni, "getgrent_r"); +- nss_endgrent = __nss_lookup_function (ni, "endgrent"); +- } +-} +- +-static enum nss_status +-internal_setgrent (ent_t *ent, int stayopen, int needent) +-{ +- enum nss_status status = NSS_STATUS_SUCCESS; +- +- ent->files = TRUE; +- +- if (ent->blacklist.data != NULL) +- { +- ent->blacklist.current = 1; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- } +- else +- ent->blacklist.current = 0; +- +- if (ent->stream == NULL) +- { +- ent->stream = fopen ("/etc/group", "rme"); +- +- if (ent->stream == NULL) +- status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; +- else +- /* We take care of locking ourself. */ +- __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); +- } +- else +- rewind (ent->stream); +- +- if (needent && status == NSS_STATUS_SUCCESS && nss_setgrent) +- ent->setent_status = nss_setgrent (stayopen); +- +- return status; +-} +- +- +-enum nss_status +-_nss_compat_setgrent (int stayopen) +-{ +- enum nss_status result; +- +- __libc_lock_lock (lock); +- +- if (ni == NULL) +- init_nss_interface (); +- +- result = internal_setgrent (&ext_ent, stayopen, 1); +- +- __libc_lock_unlock (lock); +- +- return result; +-} +- +- +-static enum nss_status +-internal_endgrent (ent_t *ent) +-{ +- if (ent->stream != NULL) +- { +- fclose (ent->stream); +- ent->stream = NULL; +- } +- +- if (ent->blacklist.data != NULL) +- { +- ent->blacklist.current = 1; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- } +- else +- ent->blacklist.current = 0; +- +- return NSS_STATUS_SUCCESS; +-} +- +-enum nss_status +-_nss_compat_endgrent (void) +-{ +- enum nss_status result; +- +- __libc_lock_lock (lock); +- +- if (nss_endgrent) +- nss_endgrent (); +- +- result = internal_endgrent (&ext_ent); +- +- __libc_lock_unlock (lock); +- +- return result; +-} +- +-/* get the next group from NSS (+ entry) */ +-static enum nss_status +-getgrent_next_nss (struct group *result, ent_t *ent, char *buffer, +- size_t buflen, int *errnop) +-{ +- if (!nss_getgrent_r) +- return NSS_STATUS_UNAVAIL; +- +- /* If the setgrent call failed, say so. */ +- if (ent->setent_status != NSS_STATUS_SUCCESS) +- return ent->setent_status; +- +- do +- { +- enum nss_status status; +- +- if ((status = nss_getgrent_r (result, buffer, buflen, errnop)) != +- NSS_STATUS_SUCCESS) +- return status; +- } +- while (in_blacklist (result->gr_name, strlen (result->gr_name), ent)); +- +- return NSS_STATUS_SUCCESS; +-} +- +-/* This function handle the +group entrys in /etc/group */ +-static enum nss_status +-getgrnam_plusgroup (const char *name, struct group *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- if (!nss_getgrnam_r) +- return NSS_STATUS_UNAVAIL; +- +- enum nss_status status = nss_getgrnam_r (name, result, buffer, buflen, +- errnop); +- if (status != NSS_STATUS_SUCCESS) +- return status; +- +- if (in_blacklist (result->gr_name, strlen (result->gr_name), ent)) +- return NSS_STATUS_NOTFOUND; +- +- /* We found the entry. */ +- return NSS_STATUS_SUCCESS; +-} +- +-static enum nss_status +-getgrent_next_file (struct group *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- struct parser_data *data = (void *) buffer; +- while (1) +- { +- fpos_t pos; +- int parse_res = 0; +- char *p; +- +- do +- { +- /* We need at least 3 characters for one line. */ +- if (__glibc_unlikely (buflen < 3)) +- { +- erange: +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- +- fgetpos (ent->stream, &pos); +- buffer[buflen - 1] = '\xff'; +- p = fgets_unlocked (buffer, buflen, ent->stream); +- if (p == NULL && feof_unlocked (ent->stream)) +- return NSS_STATUS_NOTFOUND; +- +- if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) +- { +- erange_reset: +- fsetpos (ent->stream, &pos); +- goto erange; +- } +- +- /* Terminate the line for any case. */ +- buffer[buflen - 1] = '\0'; +- +- /* Skip leading blanks. */ +- while (isspace (*p)) +- ++p; +- } +- while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ +- /* Parse the line. If it is invalid, loop to +- get the next line of the file to parse. */ +- !(parse_res = _nss_files_parse_grent (p, result, data, buflen, +- errnop))); +- +- if (__glibc_unlikely (parse_res == -1)) +- /* The parser ran out of space. */ +- goto erange_reset; +- +- if (result->gr_name[0] != '+' && result->gr_name[0] != '-') +- /* This is a real entry. */ +- break; +- +- /* -group */ +- if (result->gr_name[0] == '-' && result->gr_name[1] != '\0' +- && result->gr_name[1] != '@') +- { +- blacklist_store_name (&result->gr_name[1], ent); +- continue; +- } +- +- /* +group */ +- if (result->gr_name[0] == '+' && result->gr_name[1] != '\0' +- && result->gr_name[1] != '@') +- { +- size_t len = strlen (result->gr_name); +- char buf[len]; +- enum nss_status status; +- +- /* Store the group in the blacklist for the "+" at the end of +- /etc/group */ +- memcpy (buf, &result->gr_name[1], len); +- status = getgrnam_plusgroup (&result->gr_name[1], result, ent, +- buffer, buflen, errnop); +- blacklist_store_name (buf, ent); +- if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ +- break; +- else if (status == NSS_STATUS_RETURN /* We couldn't parse the entry*/ +- || status == NSS_STATUS_NOTFOUND) /* No group in NIS */ +- continue; +- else +- { +- if (status == NSS_STATUS_TRYAGAIN) +- /* The parser ran out of space. */ +- goto erange_reset; +- +- return status; +- } +- } +- +- /* +:... */ +- if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') +- { +- ent->files = FALSE; +- +- return getgrent_next_nss (result, ent, buffer, buflen, errnop); +- } +- } +- +- return NSS_STATUS_SUCCESS; +-} +- +- +-enum nss_status +-_nss_compat_getgrent_r (struct group *grp, char *buffer, size_t buflen, +- int *errnop) +-{ +- enum nss_status result = NSS_STATUS_SUCCESS; +- +- __libc_lock_lock (lock); +- +- /* Be prepared that the setgrent function was not called before. */ +- if (ni == NULL) +- init_nss_interface (); +- +- if (ext_ent.stream == NULL) +- result = internal_setgrent (&ext_ent, 1, 1); +- +- if (result == NSS_STATUS_SUCCESS) +- { +- if (ext_ent.files) +- result = getgrent_next_file (grp, &ext_ent, buffer, buflen, errnop); +- else +- result = getgrent_next_nss (grp, &ext_ent, buffer, buflen, errnop); +- } +- __libc_lock_unlock (lock); +- +- return result; +-} +- +-/* Searches in /etc/group and the NIS/NIS+ map for a special group */ +-static enum nss_status +-internal_getgrnam_r (const char *name, struct group *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- struct parser_data *data = (void *) buffer; +- while (1) +- { +- fpos_t pos; +- int parse_res = 0; +- char *p; +- +- do +- { +- /* We need at least 3 characters for one line. */ +- if (__glibc_unlikely (buflen < 3)) +- { +- erange: +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- +- fgetpos (ent->stream, &pos); +- buffer[buflen - 1] = '\xff'; +- p = fgets_unlocked (buffer, buflen, ent->stream); +- if (p == NULL && feof_unlocked (ent->stream)) +- return NSS_STATUS_NOTFOUND; +- +- if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) +- { +- erange_reset: +- fsetpos (ent->stream, &pos); +- goto erange; +- } +- +- /* Terminate the line for any case. */ +- buffer[buflen - 1] = '\0'; +- +- /* Skip leading blanks. */ +- while (isspace (*p)) +- ++p; +- } +- while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ +- /* Parse the line. If it is invalid, loop to +- get the next line of the file to parse. */ +- !(parse_res = _nss_files_parse_grent (p, result, data, buflen, +- errnop))); +- +- if (__glibc_unlikely (parse_res == -1)) +- /* The parser ran out of space. */ +- goto erange_reset; +- +- /* This is a real entry. */ +- if (result->gr_name[0] != '+' && result->gr_name[0] != '-') +- { +- if (strcmp (result->gr_name, name) == 0) +- return NSS_STATUS_SUCCESS; +- else +- continue; +- } +- +- /* -group */ +- if (result->gr_name[0] == '-' && result->gr_name[1] != '\0') +- { +- if (strcmp (&result->gr_name[1], name) == 0) +- return NSS_STATUS_NOTFOUND; +- else +- continue; +- } +- +- /* +group */ +- if (result->gr_name[0] == '+' && result->gr_name[1] != '\0') +- { +- if (strcmp (name, &result->gr_name[1]) == 0) +- { +- enum nss_status status; +- +- status = getgrnam_plusgroup (name, result, ent, +- buffer, buflen, errnop); +- if (status == NSS_STATUS_RETURN) +- /* We couldn't parse the entry */ +- continue; +- else +- return status; +- } +- } +- /* +:... */ +- if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') +- { +- enum nss_status status; +- +- status = getgrnam_plusgroup (name, result, ent, +- buffer, buflen, errnop); +- if (status == NSS_STATUS_RETURN) +- /* We couldn't parse the entry */ +- continue; +- else +- return status; +- } +- } +- +- return NSS_STATUS_SUCCESS; +-} +- +-enum nss_status +-_nss_compat_getgrnam_r (const char *name, struct group *grp, +- char *buffer, size_t buflen, int *errnop) +-{ +- ent_t ent = { TRUE, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; +- enum nss_status result; +- +- if (name[0] == '-' || name[0] == '+') +- return NSS_STATUS_NOTFOUND; +- +- __libc_lock_lock (lock); +- +- if (ni == NULL) +- init_nss_interface (); +- +- __libc_lock_unlock (lock); +- +- result = internal_setgrent (&ent, 0, 0); +- +- if (result == NSS_STATUS_SUCCESS) +- result = internal_getgrnam_r (name, grp, &ent, buffer, buflen, errnop); +- +- internal_endgrent (&ent); +- +- return result; +-} +- +-/* Searches in /etc/group and the NIS/NIS+ map for a special group id */ +-static enum nss_status +-internal_getgrgid_r (gid_t gid, struct group *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- struct parser_data *data = (void *) buffer; +- while (1) +- { +- fpos_t pos; +- int parse_res = 0; +- char *p; +- +- do +- { +- /* We need at least 3 characters for one line. */ +- if (__glibc_unlikely (buflen < 3)) +- { +- erange: +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- +- fgetpos (ent->stream, &pos); +- buffer[buflen - 1] = '\xff'; +- p = fgets_unlocked (buffer, buflen, ent->stream); +- if (p == NULL && feof_unlocked (ent->stream)) +- return NSS_STATUS_NOTFOUND; +- +- if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) +- { +- erange_reset: +- fsetpos (ent->stream, &pos); +- goto erange; +- } +- +- /* Terminate the line for any case. */ +- buffer[buflen - 1] = '\0'; +- +- /* Skip leading blanks. */ +- while (isspace (*p)) +- ++p; +- } +- while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ +- /* Parse the line. If it is invalid, loop to +- get the next line of the file to parse. */ +- !(parse_res = _nss_files_parse_grent (p, result, data, buflen, +- errnop))); +- +- if (__glibc_unlikely (parse_res == -1)) +- /* The parser ran out of space. */ +- goto erange_reset; +- +- /* This is a real entry. */ +- if (result->gr_name[0] != '+' && result->gr_name[0] != '-') +- { +- if (result->gr_gid == gid) +- return NSS_STATUS_SUCCESS; +- else +- continue; +- } +- +- /* -group */ +- if (result->gr_name[0] == '-' && result->gr_name[1] != '\0') +- { +- blacklist_store_name (&result->gr_name[1], ent); +- continue; +- } +- +- /* +group */ +- if (result->gr_name[0] == '+' && result->gr_name[1] != '\0') +- { +- /* Yes, no +1, see the memcpy call below. */ +- size_t len = strlen (result->gr_name); +- char buf[len]; +- enum nss_status status; +- +- /* Store the group in the blacklist for the "+" at the end of +- /etc/group */ +- memcpy (buf, &result->gr_name[1], len); +- status = getgrnam_plusgroup (&result->gr_name[1], result, ent, +- buffer, buflen, errnop); +- blacklist_store_name (buf, ent); +- if (status == NSS_STATUS_SUCCESS && result->gr_gid == gid) +- break; +- else +- continue; +- } +- /* +:... */ +- if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') +- { +- if (!nss_getgrgid_r) +- return NSS_STATUS_UNAVAIL; +- +- enum nss_status status = nss_getgrgid_r (gid, result, buffer, buflen, +- errnop); +- if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ +- return NSS_STATUS_NOTFOUND; +- else +- return status; +- } +- } +- +- return NSS_STATUS_SUCCESS; +-} +- +-enum nss_status +-_nss_compat_getgrgid_r (gid_t gid, struct group *grp, +- char *buffer, size_t buflen, int *errnop) +-{ +- ent_t ent = { TRUE, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; +- enum nss_status result; +- +- __libc_lock_lock (lock); +- +- if (ni == NULL) +- init_nss_interface (); +- +- __libc_lock_unlock (lock); +- +- result = internal_setgrent (&ent, 0, 0); +- +- if (result == NSS_STATUS_SUCCESS) +- result = internal_getgrgid_r (gid, grp, &ent, buffer, buflen, errnop); +- +- internal_endgrent (&ent); +- +- return result; +-} +- +- +-/* Support routines for remembering -@netgroup and -user entries. +- The names are stored in a single string with `|' as separator. */ +-static void +-blacklist_store_name (const char *name, ent_t *ent) +-{ +- int namelen = strlen (name); +- char *tmp; +- +- /* first call, setup cache */ +- if (ent->blacklist.size == 0) +- { +- ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); +- ent->blacklist.data = malloc (ent->blacklist.size); +- if (ent->blacklist.data == NULL) +- return; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- ent->blacklist.current = 1; +- } +- else +- { +- if (in_blacklist (name, namelen, ent)) +- return; /* no duplicates */ +- +- if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) +- { +- ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); +- tmp = realloc (ent->blacklist.data, ent->blacklist.size); +- if (tmp == NULL) +- { +- free (ent->blacklist.data); +- ent->blacklist.size = 0; +- return; +- } +- ent->blacklist.data = tmp; +- } +- } +- +- tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); +- *tmp++ = '|'; +- *tmp = '\0'; +- ent->blacklist.current += namelen + 1; +- +- return; +-} +- +-/* returns TRUE if ent->blacklist contains name, else FALSE */ +-static bool_t +-in_blacklist (const char *name, int namelen, ent_t *ent) +-{ +- char buf[namelen + 3]; +- char *cp; +- +- if (ent->blacklist.data == NULL) +- return FALSE; +- +- buf[0] = '|'; +- cp = stpcpy (&buf[1], name); +- *cp++ = '|'; +- *cp = '\0'; +- return strstr (ent->blacklist.data, buf) != NULL; +-} +Index: glibc-2.26/nis/nss_compat/compat-initgroups.c +=================================================================== +--- glibc-2.26.orig/nis/nss_compat/compat-initgroups.c ++++ /dev/null +@@ -1,576 +0,0 @@ +-/* Copyright (C) 1998-2017 Free Software Foundation, Inc. +- This file is part of the GNU C Library. +- Contributed by Thorsten Kukuk , 1998. +- +- 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 +-#include +- +-static service_user *ni; +-/* Type of the lookup function. */ +-static enum nss_status (*nss_initgroups_dyn) (const char *, gid_t, +- long int *, long int *, +- gid_t **, long int, int *); +-static enum nss_status (*nss_getgrnam_r) (const char *name, +- struct group * grp, char *buffer, +- size_t buflen, int *errnop); +-static enum nss_status (*nss_getgrgid_r) (gid_t gid, struct group * grp, +- char *buffer, size_t buflen, +- int *errnop); +-static enum nss_status (*nss_setgrent) (int stayopen); +-static enum nss_status (*nss_getgrent_r) (struct group * grp, char *buffer, +- size_t buflen, int *errnop); +-static enum nss_status (*nss_endgrent) (void); +- +-/* Protect global state against multiple changers. */ +-__libc_lock_define_initialized (static, lock) +- +- +-/* Get the declaration of the parser function. */ +-#define ENTNAME grent +-#define STRUCTURE group +-#define EXTERN_PARSER +-#include +- +-/* Structure for remembering -group members ... */ +-#define BLACKLIST_INITIAL_SIZE 512 +-#define BLACKLIST_INCREMENT 256 +-struct blacklist_t +-{ +- char *data; +- int current; +- int size; +-}; +- +-struct ent_t +-{ +- bool files; +- bool need_endgrent; +- bool skip_initgroups_dyn; +- FILE *stream; +- struct blacklist_t blacklist; +-}; +-typedef struct ent_t ent_t; +- +-/* Prototypes for local functions. */ +-static void blacklist_store_name (const char *, ent_t *); +-static int in_blacklist (const char *, int, ent_t *); +- +-/* Initialize the NSS interface/functions. The calling function must +- hold the lock. */ +-static void +-init_nss_interface (void) +-{ +- __libc_lock_lock (lock); +- +- /* Retest. */ +- if (ni == NULL +- && __nss_database_lookup ("group_compat", NULL, "nis", &ni) >= 0) +- { +- nss_initgroups_dyn = __nss_lookup_function (ni, "initgroups_dyn"); +- nss_getgrnam_r = __nss_lookup_function (ni, "getgrnam_r"); +- nss_getgrgid_r = __nss_lookup_function (ni, "getgrgid_r"); +- nss_setgrent = __nss_lookup_function (ni, "setgrent"); +- nss_getgrent_r = __nss_lookup_function (ni, "getgrent_r"); +- nss_endgrent = __nss_lookup_function (ni, "endgrent"); +- } +- +- __libc_lock_unlock (lock); +-} +- +-static enum nss_status +-internal_setgrent (ent_t *ent) +-{ +- enum nss_status status = NSS_STATUS_SUCCESS; +- +- ent->files = true; +- +- if (ni == NULL) +- init_nss_interface (); +- +- if (ent->blacklist.data != NULL) +- { +- ent->blacklist.current = 1; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- } +- else +- ent->blacklist.current = 0; +- +- ent->stream = fopen ("/etc/group", "rme"); +- +- if (ent->stream == NULL) +- status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; +- else +- /* We take care of locking ourself. */ +- __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); +- +- return status; +-} +- +- +-static enum nss_status +-internal_endgrent (ent_t *ent) +-{ +- if (ent->stream != NULL) +- { +- fclose (ent->stream); +- ent->stream = NULL; +- } +- +- if (ent->blacklist.data != NULL) +- { +- ent->blacklist.current = 1; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- } +- else +- ent->blacklist.current = 0; +- +- if (ent->need_endgrent && nss_endgrent != NULL) +- nss_endgrent (); +- +- return NSS_STATUS_SUCCESS; +-} +- +-/* Add new group record. */ +-static void +-add_group (long int *start, long int *size, gid_t **groupsp, long int limit, +- gid_t gid) +-{ +- gid_t *groups = *groupsp; +- +- /* Matches user. 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. */ +- return; +- +- if (limit <= 0) +- newsize = 2 * *size; +- else +- newsize = MIN (limit, 2 * *size); +- +- newgroups = realloc (groups, newsize * sizeof (*groups)); +- if (newgroups == NULL) +- return; +- *groupsp = groups = newgroups; +- *size = newsize; +- } +- +- groups[*start] = gid; +- *start += 1; +-} +- +-/* This function checks, if the user is a member of this group and if +- yes, add the group id to the list. Return nonzero is we couldn't +- handle the group because the user is not in the member list. */ +-static int +-check_and_add_group (const char *user, gid_t group, long int *start, +- long int *size, gid_t **groupsp, long int limit, +- struct group *grp) +-{ +- char **member; +- +- /* Don't add main group to list of groups. */ +- if (grp->gr_gid == group) +- return 0; +- +- for (member = grp->gr_mem; *member != NULL; ++member) +- if (strcmp (*member, user) == 0) +- { +- add_group (start, size, groupsp, limit, grp->gr_gid); +- return 0; +- } +- +- return 1; +-} +- +-/* Get the next group from NSS (+ entry). If the NSS module supports +- initgroups_dyn, get all entries at once. */ +-static enum nss_status +-getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user, +- gid_t group, long int *start, long int *size, +- gid_t **groupsp, long int limit, int *errnop) +-{ +- enum nss_status status; +- struct group grpbuf; +- +- /* Try nss_initgroups_dyn if supported. We also need getgrgid_r. +- If this function is not supported, step through the whole group +- database with getgrent_r. */ +- if (! ent->skip_initgroups_dyn) +- { +- long int mystart = 0; +- long int mysize = limit <= 0 ? *size : limit; +- gid_t *mygroups = malloc (mysize * sizeof (gid_t)); +- +- if (mygroups == NULL) +- return NSS_STATUS_TRYAGAIN; +- +- /* For every gid in the list we get from the NSS module, +- get the whole group entry. We need to do this, since we +- need the group name to check if it is in the blacklist. +- In worst case, this is as twice as slow as stepping with +- getgrent_r through the whole group database. But for large +- group databases this is faster, since the user can only be +- in a limited number of groups. */ +- if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups, +- limit, errnop) == NSS_STATUS_SUCCESS) +- { +- status = NSS_STATUS_NOTFOUND; +- +- /* If there is no blacklist we can trust the underlying +- initgroups implementation. */ +- if (ent->blacklist.current <= 1) +- for (int i = 0; i < mystart; i++) +- add_group (start, size, groupsp, limit, mygroups[i]); +- else +- { +- /* A temporary buffer. We use the normal buffer, until we find +- an entry, for which this buffer is to small. In this case, we +- overwrite the pointer with one to a bigger buffer. */ +- char *tmpbuf = buffer; +- size_t tmplen = buflen; +- bool use_malloc = false; +- +- for (int i = 0; i < mystart; i++) +- { +- while ((status = nss_getgrgid_r (mygroups[i], &grpbuf, +- tmpbuf, tmplen, errnop)) +- == NSS_STATUS_TRYAGAIN +- && *errnop == ERANGE) +- { +- if (__libc_use_alloca (tmplen * 2)) +- { +- if (tmpbuf == buffer) +- { +- tmplen *= 2; +- tmpbuf = __alloca (tmplen); +- } +- else +- tmpbuf = extend_alloca (tmpbuf, tmplen, tmplen * 2); +- } +- else +- { +- tmplen *= 2; +- char *newbuf = realloc (use_malloc ? tmpbuf : NULL, tmplen); +- +- if (newbuf == NULL) +- { +- status = NSS_STATUS_TRYAGAIN; +- goto done; +- } +- use_malloc = true; +- tmpbuf = newbuf; +- } +- } +- +- if (__builtin_expect (status != NSS_STATUS_NOTFOUND, 1)) +- { +- if (__builtin_expect (status != NSS_STATUS_SUCCESS, 0)) +- goto done; +- +- if (!in_blacklist (grpbuf.gr_name, +- strlen (grpbuf.gr_name), ent) +- && check_and_add_group (user, group, start, size, +- groupsp, limit, &grpbuf)) +- { +- if (nss_setgrent != NULL) +- { +- nss_setgrent (1); +- ent->need_endgrent = true; +- } +- ent->skip_initgroups_dyn = true; +- +- goto iter; +- } +- } +- } +- +- status = NSS_STATUS_NOTFOUND; +- +- done: +- if (use_malloc) +- free (tmpbuf); +- } +- +- free (mygroups); +- +- return status; +- } +- +- free (mygroups); +- } +- +- /* If we come here, the NSS module does not support initgroups_dyn +- or we were confronted with a split group. In these cases we have +- to step through the whole list ourself. */ +- iter: +- do +- { +- if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) != +- NSS_STATUS_SUCCESS) +- break; +- } +- while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent)); +- +- if (status == NSS_STATUS_SUCCESS) +- check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf); +- +- return status; +-} +- +-static enum nss_status +-internal_getgrent_r (ent_t *ent, char *buffer, size_t buflen, const char *user, +- gid_t group, long int *start, long int *size, +- gid_t **groupsp, long int limit, int *errnop) +-{ +- struct parser_data *data = (void *) buffer; +- struct group grpbuf; +- +- if (!ent->files) +- return getgrent_next_nss (ent, buffer, buflen, user, group, +- start, size, groupsp, limit, errnop); +- +- while (1) +- { +- fpos_t pos; +- int parse_res = 0; +- char *p; +- +- do +- { +- /* We need at least 3 characters for one line. */ +- if (__glibc_unlikely (buflen < 3)) +- { +- erange: +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- +- fgetpos (ent->stream, &pos); +- buffer[buflen - 1] = '\xff'; +- p = fgets_unlocked (buffer, buflen, ent->stream); +- if (p == NULL && feof_unlocked (ent->stream)) +- return NSS_STATUS_NOTFOUND; +- +- if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) +- { +- erange_reset: +- fsetpos (ent->stream, &pos); +- goto erange; +- } +- +- /* Terminate the line for any case. */ +- buffer[buflen - 1] = '\0'; +- +- /* Skip leading blanks. */ +- while (isspace (*p)) +- ++p; +- } +- while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ +- /* Parse the line. If it is invalid, loop to +- get the next line of the file to parse. */ +- !(parse_res = _nss_files_parse_grent (p, &grpbuf, data, buflen, +- errnop))); +- +- if (__glibc_unlikely (parse_res == -1)) +- /* The parser ran out of space. */ +- goto erange_reset; +- +- if (grpbuf.gr_name[0] != '+' && grpbuf.gr_name[0] != '-') +- /* This is a real entry. */ +- break; +- +- /* -group */ +- if (grpbuf.gr_name[0] == '-' && grpbuf.gr_name[1] != '\0' +- && grpbuf.gr_name[1] != '@') +- { +- blacklist_store_name (&grpbuf.gr_name[1], ent); +- continue; +- } +- +- /* +group */ +- if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] != '\0' +- && grpbuf.gr_name[1] != '@') +- { +- if (in_blacklist (&grpbuf.gr_name[1], +- strlen (&grpbuf.gr_name[1]), ent)) +- continue; +- /* Store the group in the blacklist for the "+" at the end of +- /etc/group */ +- blacklist_store_name (&grpbuf.gr_name[1], ent); +- if (nss_getgrnam_r == NULL) +- return NSS_STATUS_UNAVAIL; +- else if (nss_getgrnam_r (&grpbuf.gr_name[1], &grpbuf, buffer, +- buflen, errnop) != NSS_STATUS_SUCCESS) +- continue; +- +- check_and_add_group (user, group, start, size, groupsp, +- limit, &grpbuf); +- +- return NSS_STATUS_SUCCESS; +- } +- +- /* +:... */ +- if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] == '\0') +- { +- /* If the selected module does not support getgrent_r or +- initgroups_dyn, abort. We cannot find the needed group +- entries. */ +- if (nss_initgroups_dyn == NULL || nss_getgrgid_r == NULL) +- { +- if (nss_setgrent != NULL) +- { +- nss_setgrent (1); +- ent->need_endgrent = true; +- } +- ent->skip_initgroups_dyn = true; +- +- if (nss_getgrent_r == NULL) +- return NSS_STATUS_UNAVAIL; +- } +- +- ent->files = false; +- +- return getgrent_next_nss (ent, buffer, buflen, user, group, +- start, size, groupsp, limit, errnop); +- } +- } +- +- check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf); +- +- return NSS_STATUS_SUCCESS; +-} +- +- +-enum nss_status +-_nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start, +- long int *size, gid_t **groupsp, long int limit, +- int *errnop) +-{ +- enum nss_status status; +- ent_t intern = { true, false, false, NULL, {NULL, 0, 0} }; +- +- status = internal_setgrent (&intern); +- if (status != NSS_STATUS_SUCCESS) +- return status; +- +- struct scratch_buffer tmpbuf; +- scratch_buffer_init (&tmpbuf); +- +- do +- { +- while ((status = internal_getgrent_r (&intern, tmpbuf.data, tmpbuf.length, +- user, group, start, size, +- groupsp, limit, errnop)) +- == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) +- if (!scratch_buffer_grow (&tmpbuf)) +- goto done; +- } +- while (status == NSS_STATUS_SUCCESS); +- +- status = NSS_STATUS_SUCCESS; +- +- done: +- scratch_buffer_free (&tmpbuf); +- +- internal_endgrent (&intern); +- +- return status; +-} +- +- +-/* Support routines for remembering -@netgroup and -user entries. +- The names are stored in a single string with `|' as separator. */ +-static void +-blacklist_store_name (const char *name, ent_t *ent) +-{ +- int namelen = strlen (name); +- char *tmp; +- +- /* First call, setup cache. */ +- if (ent->blacklist.size == 0) +- { +- ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); +- ent->blacklist.data = malloc (ent->blacklist.size); +- if (ent->blacklist.data == NULL) +- return; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- ent->blacklist.current = 1; +- } +- else +- { +- if (in_blacklist (name, namelen, ent)) +- return; /* no duplicates */ +- +- if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) +- { +- ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); +- tmp = realloc (ent->blacklist.data, ent->blacklist.size); +- if (tmp == NULL) +- { +- free (ent->blacklist.data); +- ent->blacklist.size = 0; +- return; +- } +- ent->blacklist.data = tmp; +- } +- } +- +- tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); +- *tmp++ = '|'; +- *tmp = '\0'; +- ent->blacklist.current += namelen + 1; +- +- return; +-} +- +-/* returns TRUE if ent->blacklist contains name, else FALSE */ +-static bool_t +-in_blacklist (const char *name, int namelen, ent_t *ent) +-{ +- char buf[namelen + 3]; +- char *cp; +- +- if (ent->blacklist.data == NULL) +- return FALSE; +- +- buf[0] = '|'; +- cp = stpcpy (&buf[1], name); +- *cp++ = '|'; +- *cp = '\0'; +- return strstr (ent->blacklist.data, buf) != NULL; +-} +Index: glibc-2.26/nis/nss_compat/compat-pwd.c +=================================================================== +--- glibc-2.26.orig/nis/nss_compat/compat-pwd.c ++++ /dev/null +@@ -1,1132 +0,0 @@ +-/* Copyright (C) 1996-2017 Free Software Foundation, Inc. +- This file is part of the GNU C Library. +- Contributed by Thorsten Kukuk , 1996. +- +- 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 +- +-#include "netgroup.h" +- +-static service_user *ni; +-static enum nss_status (*nss_setpwent) (int stayopen); +-static enum nss_status (*nss_getpwnam_r) (const char *name, +- struct passwd * pwd, char *buffer, +- size_t buflen, int *errnop); +-static enum nss_status (*nss_getpwuid_r) (uid_t uid, struct passwd * pwd, +- char *buffer, size_t buflen, +- int *errnop); +-static enum nss_status (*nss_getpwent_r) (struct passwd * pwd, char *buffer, +- size_t buflen, int *errnop); +-static enum nss_status (*nss_endpwent) (void); +- +-/* Get the declaration of the parser function. */ +-#define ENTNAME pwent +-#define STRUCTURE passwd +-#define EXTERN_PARSER +-#include +- +-/* Structure for remembering -@netgroup and -user members ... */ +-#define BLACKLIST_INITIAL_SIZE 512 +-#define BLACKLIST_INCREMENT 256 +-struct blacklist_t +-{ +- char *data; +- int current; +- int size; +-}; +- +-struct ent_t +-{ +- bool netgroup; +- bool first; +- bool files; +- enum nss_status setent_status; +- FILE *stream; +- struct blacklist_t blacklist; +- struct passwd pwd; +- struct __netgrent netgrdata; +-}; +-typedef struct ent_t ent_t; +- +-static ent_t ext_ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, +- { NULL, 0, 0 }, +- { NULL, NULL, 0, 0, NULL, NULL, NULL }}; +- +-/* Protect global state against multiple changers. */ +-__libc_lock_define_initialized (static, lock) +- +-/* Prototypes for local functions. */ +-static void blacklist_store_name (const char *, ent_t *); +-static int in_blacklist (const char *, int, ent_t *); +- +-/* Initialize the NSS interface/functions. The calling function must +- hold the lock. */ +-static void +-init_nss_interface (void) +-{ +- if (__nss_database_lookup ("passwd_compat", NULL, "nis", &ni) >= 0) +- { +- nss_setpwent = __nss_lookup_function (ni, "setpwent"); +- nss_getpwnam_r = __nss_lookup_function (ni, "getpwnam_r"); +- nss_getpwuid_r = __nss_lookup_function (ni, "getpwuid_r"); +- nss_getpwent_r = __nss_lookup_function (ni, "getpwent_r"); +- nss_endpwent = __nss_lookup_function (ni, "endpwent"); +- } +-} +- +-static void +-give_pwd_free (struct passwd *pwd) +-{ +- free (pwd->pw_name); +- free (pwd->pw_passwd); +- free (pwd->pw_gecos); +- free (pwd->pw_dir); +- free (pwd->pw_shell); +- +- memset (pwd, '\0', sizeof (struct passwd)); +-} +- +-static size_t +-pwd_need_buflen (struct passwd *pwd) +-{ +- size_t len = 0; +- +- if (pwd->pw_passwd != NULL) +- len += strlen (pwd->pw_passwd) + 1; +- +- if (pwd->pw_gecos != NULL) +- len += strlen (pwd->pw_gecos) + 1; +- +- if (pwd->pw_dir != NULL) +- len += strlen (pwd->pw_dir) + 1; +- +- if (pwd->pw_shell != NULL) +- len += strlen (pwd->pw_shell) + 1; +- +- return len; +-} +- +-static void +-copy_pwd_changes (struct passwd *dest, struct passwd *src, +- char *buffer, size_t buflen) +-{ +- if (src->pw_passwd != NULL && strlen (src->pw_passwd)) +- { +- if (buffer == NULL) +- dest->pw_passwd = strdup (src->pw_passwd); +- else if (dest->pw_passwd && +- strlen (dest->pw_passwd) >= strlen (src->pw_passwd)) +- strcpy (dest->pw_passwd, src->pw_passwd); +- else +- { +- dest->pw_passwd = buffer; +- strcpy (dest->pw_passwd, src->pw_passwd); +- buffer += strlen (dest->pw_passwd) + 1; +- buflen = buflen - (strlen (dest->pw_passwd) + 1); +- } +- } +- +- if (src->pw_gecos != NULL && strlen (src->pw_gecos)) +- { +- if (buffer == NULL) +- dest->pw_gecos = strdup (src->pw_gecos); +- else if (dest->pw_gecos && +- strlen (dest->pw_gecos) >= strlen (src->pw_gecos)) +- strcpy (dest->pw_gecos, src->pw_gecos); +- else +- { +- dest->pw_gecos = buffer; +- strcpy (dest->pw_gecos, src->pw_gecos); +- buffer += strlen (dest->pw_gecos) + 1; +- buflen = buflen - (strlen (dest->pw_gecos) + 1); +- } +- } +- if (src->pw_dir != NULL && strlen (src->pw_dir)) +- { +- if (buffer == NULL) +- dest->pw_dir = strdup (src->pw_dir); +- else if (dest->pw_dir && strlen (dest->pw_dir) >= strlen (src->pw_dir)) +- strcpy (dest->pw_dir, src->pw_dir); +- else +- { +- dest->pw_dir = buffer; +- strcpy (dest->pw_dir, src->pw_dir); +- buffer += strlen (dest->pw_dir) + 1; +- buflen = buflen - (strlen (dest->pw_dir) + 1); +- } +- } +- +- if (src->pw_shell != NULL && strlen (src->pw_shell)) +- { +- if (buffer == NULL) +- dest->pw_shell = strdup (src->pw_shell); +- else if (dest->pw_shell && +- strlen (dest->pw_shell) >= strlen (src->pw_shell)) +- strcpy (dest->pw_shell, src->pw_shell); +- else +- { +- dest->pw_shell = buffer; +- strcpy (dest->pw_shell, src->pw_shell); +- buffer += strlen (dest->pw_shell) + 1; +- buflen = buflen - (strlen (dest->pw_shell) + 1); +- } +- } +-} +- +-static enum nss_status +-internal_setpwent (ent_t *ent, int stayopen, int needent) +-{ +- enum nss_status status = NSS_STATUS_SUCCESS; +- +- ent->first = ent->netgroup = false; +- ent->files = true; +- ent->setent_status = NSS_STATUS_SUCCESS; +- +- /* If something was left over free it. */ +- if (ent->netgroup) +- __internal_endnetgrent (&ent->netgrdata); +- +- if (ent->blacklist.data != NULL) +- { +- ent->blacklist.current = 1; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- } +- else +- ent->blacklist.current = 0; +- +- if (ent->stream == NULL) +- { +- ent->stream = fopen ("/etc/passwd", "rme"); +- +- if (ent->stream == NULL) +- status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; +- else +- /* We take care of locking ourself. */ +- __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); +- } +- else +- rewind (ent->stream); +- +- give_pwd_free (&ent->pwd); +- +- if (needent && status == NSS_STATUS_SUCCESS && nss_setpwent) +- ent->setent_status = nss_setpwent (stayopen); +- +- return status; +-} +- +- +-enum nss_status +-_nss_compat_setpwent (int stayopen) +-{ +- enum nss_status result; +- +- __libc_lock_lock (lock); +- +- if (ni == NULL) +- init_nss_interface (); +- +- result = internal_setpwent (&ext_ent, stayopen, 1); +- +- __libc_lock_unlock (lock); +- +- return result; +-} +- +- +-static enum nss_status +-internal_endpwent (ent_t *ent) +-{ +- if (ent->stream != NULL) +- { +- fclose (ent->stream); +- ent->stream = NULL; +- } +- +- if (ent->netgroup) +- __internal_endnetgrent (&ent->netgrdata); +- +- ent->first = ent->netgroup = false; +- +- if (ent->blacklist.data != NULL) +- { +- ent->blacklist.current = 1; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- } +- else +- ent->blacklist.current = 0; +- +- give_pwd_free (&ent->pwd); +- +- return NSS_STATUS_SUCCESS; +-} +- +-enum nss_status +-_nss_compat_endpwent (void) +-{ +- enum nss_status result; +- +- __libc_lock_lock (lock); +- +- if (nss_endpwent) +- nss_endpwent (); +- +- result = internal_endpwent (&ext_ent); +- +- __libc_lock_unlock (lock); +- +- return result; +-} +- +- +-static enum nss_status +-getpwent_next_nss_netgr (const char *name, struct passwd *result, ent_t *ent, +- char *group, char *buffer, size_t buflen, +- int *errnop) +-{ +- char *curdomain = NULL, *host, *user, *domain, *p2; +- int status; +- size_t p2len; +- +- /* Leave function if NSS module does not support getpwnam_r, +- we need this function here. */ +- if (!nss_getpwnam_r) +- return NSS_STATUS_UNAVAIL; +- +- if (ent->first) +- { +- memset (&ent->netgrdata, 0, sizeof (struct __netgrent)); +- __internal_setnetgrent (group, &ent->netgrdata); +- ent->first = false; +- } +- +- while (1) +- { +- status = __internal_getnetgrent_r (&host, &user, &domain, +- &ent->netgrdata, buffer, buflen, +- errnop); +- if (status != 1) +- { +- __internal_endnetgrent (&ent->netgrdata); +- ent->netgroup = 0; +- give_pwd_free (&ent->pwd); +- return NSS_STATUS_RETURN; +- } +- +- if (user == NULL || user[0] == '-') +- continue; +- +- if (domain != NULL) +- { +- if (curdomain == NULL +- && yp_get_default_domain (&curdomain) != YPERR_SUCCESS) +- { +- __internal_endnetgrent (&ent->netgrdata); +- ent->netgroup = false; +- give_pwd_free (&ent->pwd); +- return NSS_STATUS_UNAVAIL; +- } +- if (strcmp (curdomain, domain) != 0) +- continue; +- } +- +- /* If name != NULL, we are called from getpwnam. */ +- if (name != NULL) +- if (strcmp (user, name) != 0) +- continue; +- +- p2len = pwd_need_buflen (&ent->pwd); +- if (p2len > buflen) +- { +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- p2 = buffer + (buflen - p2len); +- buflen -= p2len; +- +- if (nss_getpwnam_r (user, result, buffer, buflen, errnop) != +- NSS_STATUS_SUCCESS) +- continue; +- +- if (!in_blacklist (result->pw_name, strlen (result->pw_name), ent)) +- { +- /* Store the User in the blacklist for possible the "+" at the +- end of /etc/passwd */ +- blacklist_store_name (result->pw_name, ent); +- copy_pwd_changes (result, &ent->pwd, p2, p2len); +- break; +- } +- } +- +- return NSS_STATUS_SUCCESS; +-} +- +-/* get the next user from NSS (+ entry) */ +-static enum nss_status +-getpwent_next_nss (struct passwd *result, ent_t *ent, char *buffer, +- size_t buflen, int *errnop) +-{ +- enum nss_status status; +- char *p2; +- size_t p2len; +- +- /* Return if NSS module does not support getpwent_r. */ +- if (!nss_getpwent_r) +- return NSS_STATUS_UNAVAIL; +- +- /* If the setpwent call failed, say so. */ +- if (ent->setent_status != NSS_STATUS_SUCCESS) +- return ent->setent_status; +- +- p2len = pwd_need_buflen (&ent->pwd); +- if (p2len > buflen) +- { +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- p2 = buffer + (buflen - p2len); +- buflen -= p2len; +- +- if (ent->first) +- ent->first = false; +- +- do +- { +- if ((status = nss_getpwent_r (result, buffer, buflen, errnop)) != +- NSS_STATUS_SUCCESS) +- return status; +- } +- while (in_blacklist (result->pw_name, strlen (result->pw_name), ent)); +- +- copy_pwd_changes (result, &ent->pwd, p2, p2len); +- +- return NSS_STATUS_SUCCESS; +-} +- +-/* This function handle the +user entrys in /etc/passwd */ +-static enum nss_status +-getpwnam_plususer (const char *name, struct passwd *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- if (!nss_getpwnam_r) +- return NSS_STATUS_UNAVAIL; +- +- struct passwd pwd; +- memset (&pwd, '\0', sizeof (struct passwd)); +- +- copy_pwd_changes (&pwd, result, NULL, 0); +- +- size_t plen = pwd_need_buflen (&pwd); +- if (plen > buflen) +- { +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- char *p = buffer + (buflen - plen); +- buflen -= plen; +- +- enum nss_status status = nss_getpwnam_r (name, result, buffer, buflen, +- errnop); +- if (status != NSS_STATUS_SUCCESS) +- return status; +- +- if (in_blacklist (result->pw_name, strlen (result->pw_name), ent)) +- return NSS_STATUS_NOTFOUND; +- +- copy_pwd_changes (result, &pwd, p, plen); +- give_pwd_free (&pwd); +- /* We found the entry. */ +- return NSS_STATUS_SUCCESS; +-} +- +-static enum nss_status +-getpwent_next_file (struct passwd *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- struct parser_data *data = (void *) buffer; +- while (1) +- { +- fpos_t pos; +- char *p; +- int parse_res; +- +- do +- { +- /* We need at least 3 characters for one line. */ +- if (__glibc_unlikely (buflen < 3)) +- { +- erange: +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- +- fgetpos (ent->stream, &pos); +- buffer[buflen - 1] = '\xff'; +- p = fgets_unlocked (buffer, buflen, ent->stream); +- if (p == NULL && feof_unlocked (ent->stream)) +- return NSS_STATUS_NOTFOUND; +- +- if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) +- { +- erange_reset: +- fsetpos (ent->stream, &pos); +- goto erange; +- } +- +- /* Terminate the line for any case. */ +- buffer[buflen - 1] = '\0'; +- +- /* Skip leading blanks. */ +- while (isspace (*p)) +- ++p; +- } +- while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ +- /* Parse the line. If it is invalid, loop to +- get the next line of the file to parse. */ +- !(parse_res = _nss_files_parse_pwent (p, result, data, buflen, +- errnop))); +- +- if (__glibc_unlikely (parse_res == -1)) +- /* The parser ran out of space. */ +- goto erange_reset; +- +- if (result->pw_name[0] != '+' && result->pw_name[0] != '-') +- /* This is a real entry. */ +- break; +- +- /* -@netgroup */ +- if (result->pw_name[0] == '-' && result->pw_name[1] == '@' +- && result->pw_name[2] != '\0') +- { +- /* XXX Do not use fixed length buffer. */ +- char buf2[1024]; +- char *user, *host, *domain; +- struct __netgrent netgrdata; +- +- memset (&netgrdata, 0, sizeof (struct __netgrent)); +- __internal_setnetgrent (&result->pw_name[2], &netgrdata); +- while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata, +- buf2, sizeof (buf2), errnop)) +- { +- if (user != NULL && user[0] != '-') +- blacklist_store_name (user, ent); +- } +- __internal_endnetgrent (&netgrdata); +- continue; +- } +- +- /* +@netgroup */ +- if (result->pw_name[0] == '+' && result->pw_name[1] == '@' +- && result->pw_name[2] != '\0') +- { +- enum nss_status status; +- +- ent->netgroup = true; +- ent->first = true; +- copy_pwd_changes (&ent->pwd, result, NULL, 0); +- +- status = getpwent_next_nss_netgr (NULL, result, ent, +- &result->pw_name[2], +- buffer, buflen, errnop); +- if (status == NSS_STATUS_RETURN) +- continue; +- else +- return status; +- } +- +- /* -user */ +- if (result->pw_name[0] == '-' && result->pw_name[1] != '\0' +- && result->pw_name[1] != '@') +- { +- blacklist_store_name (&result->pw_name[1], ent); +- continue; +- } +- +- /* +user */ +- if (result->pw_name[0] == '+' && result->pw_name[1] != '\0' +- && result->pw_name[1] != '@') +- { +- size_t len = strlen (result->pw_name); +- char buf[len]; +- enum nss_status status; +- +- /* Store the User in the blacklist for the "+" at the end of +- /etc/passwd */ +- memcpy (buf, &result->pw_name[1], len); +- status = getpwnam_plususer (&result->pw_name[1], result, ent, +- buffer, buflen, errnop); +- blacklist_store_name (buf, ent); +- +- if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ +- break; +- else if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */ +- || status == NSS_STATUS_NOTFOUND) /* entry doesn't exist */ +- continue; +- else +- { +- if (status == NSS_STATUS_TRYAGAIN) +- { +- /* The parser ran out of space */ +- fsetpos (ent->stream, &pos); +- *errnop = ERANGE; +- } +- return status; +- } +- } +- +- /* +:... */ +- if (result->pw_name[0] == '+' && result->pw_name[1] == '\0') +- { +- ent->files = false; +- ent->first = true; +- copy_pwd_changes (&ent->pwd, result, NULL, 0); +- +- return getpwent_next_nss (result, ent, buffer, buflen, errnop); +- } +- } +- +- return NSS_STATUS_SUCCESS; +-} +- +- +-static enum nss_status +-internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer, +- size_t buflen, int *errnop) +-{ +- if (ent->netgroup) +- { +- enum nss_status status; +- +- /* We are searching members in a netgroup */ +- /* Since this is not the first call, we don't need the group name */ +- status = getpwent_next_nss_netgr (NULL, pw, ent, NULL, buffer, buflen, +- errnop); +- if (status == NSS_STATUS_RETURN) +- return getpwent_next_file (pw, ent, buffer, buflen, errnop); +- else +- return status; +- } +- else if (ent->files) +- return getpwent_next_file (pw, ent, buffer, buflen, errnop); +- else +- return getpwent_next_nss (pw, ent, buffer, buflen, errnop); +- +-} +- +-enum nss_status +-_nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen, +- int *errnop) +-{ +- enum nss_status result = NSS_STATUS_SUCCESS; +- +- __libc_lock_lock (lock); +- +- /* Be prepared that the setpwent function was not called before. */ +- if (ni == NULL) +- init_nss_interface (); +- +- if (ext_ent.stream == NULL) +- result = internal_setpwent (&ext_ent, 1, 1); +- +- if (result == NSS_STATUS_SUCCESS) +- result = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop); +- +- __libc_lock_unlock (lock); +- +- return result; +-} +- +-/* Searches in /etc/passwd and the NIS/NIS+ map for a special user */ +-static enum nss_status +-internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- struct parser_data *data = (void *) buffer; +- +- while (1) +- { +- fpos_t pos; +- char *p; +- int parse_res; +- +- do +- { +- /* We need at least 3 characters for one line. */ +- if (__glibc_unlikely (buflen < 3)) +- { +- erange: +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- +- fgetpos (ent->stream, &pos); +- buffer[buflen - 1] = '\xff'; +- p = fgets_unlocked (buffer, buflen, ent->stream); +- if (p == NULL && feof_unlocked (ent->stream)) +- { +- return NSS_STATUS_NOTFOUND; +- } +- if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) +- { +- erange_reset: +- fsetpos (ent->stream, &pos); +- goto erange; +- } +- +- /* Terminate the line for any case. */ +- buffer[buflen - 1] = '\0'; +- +- /* Skip leading blanks. */ +- while (isspace (*p)) +- ++p; +- } +- while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ +- /* Parse the line. If it is invalid, loop to +- get the next line of the file to parse. */ +- !(parse_res = _nss_files_parse_pwent (p, result, data, buflen, +- errnop))); +- +- if (__glibc_unlikely (parse_res == -1)) +- /* The parser ran out of space. */ +- goto erange_reset; +- +- /* This is a real entry. */ +- if (result->pw_name[0] != '+' && result->pw_name[0] != '-') +- { +- if (strcmp (result->pw_name, name) == 0) +- return NSS_STATUS_SUCCESS; +- else +- continue; +- } +- +- /* -@netgroup */ +- if (result->pw_name[0] == '-' && result->pw_name[1] == '@' +- && result->pw_name[2] != '\0') +- { +- if (innetgr (&result->pw_name[2], NULL, name, NULL)) +- return NSS_STATUS_NOTFOUND; +- continue; +- } +- +- /* +@netgroup */ +- if (result->pw_name[0] == '+' && result->pw_name[1] == '@' +- && result->pw_name[2] != '\0') +- { +- enum nss_status status; +- +- if (innetgr (&result->pw_name[2], NULL, name, NULL)) +- { +- status = getpwnam_plususer (name, result, ent, buffer, +- buflen, errnop); +- +- if (status == NSS_STATUS_RETURN) +- continue; +- +- return status; +- } +- continue; +- } +- +- /* -user */ +- if (result->pw_name[0] == '-' && result->pw_name[1] != '\0' +- && result->pw_name[1] != '@') +- { +- if (strcmp (&result->pw_name[1], name) == 0) +- return NSS_STATUS_NOTFOUND; +- else +- continue; +- } +- +- /* +user */ +- if (result->pw_name[0] == '+' && result->pw_name[1] != '\0' +- && result->pw_name[1] != '@') +- { +- if (strcmp (name, &result->pw_name[1]) == 0) +- { +- enum nss_status status; +- +- status = getpwnam_plususer (name, result, ent, buffer, buflen, +- errnop); +- if (status == NSS_STATUS_RETURN) +- /* We couldn't parse the entry */ +- return NSS_STATUS_NOTFOUND; +- else +- return status; +- } +- } +- +- /* +:... */ +- if (result->pw_name[0] == '+' && result->pw_name[1] == '\0') +- { +- enum nss_status status; +- +- status = getpwnam_plususer (name, result, ent, +- buffer, buflen, errnop); +- if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ +- break; +- else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ +- return NSS_STATUS_NOTFOUND; +- else +- return status; +- } +- } +- return NSS_STATUS_SUCCESS; +-} +- +-enum nss_status +-_nss_compat_getpwnam_r (const char *name, struct passwd *pwd, +- char *buffer, size_t buflen, int *errnop) +-{ +- enum nss_status result; +- ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }, +- { NULL, NULL, 0, 0, NULL, NULL, NULL }}; +- +- if (name[0] == '-' || name[0] == '+') +- return NSS_STATUS_NOTFOUND; +- +- __libc_lock_lock (lock); +- +- if (ni == NULL) +- init_nss_interface (); +- +- __libc_lock_unlock (lock); +- +- result = internal_setpwent (&ent, 0, 0); +- +- if (result == NSS_STATUS_SUCCESS) +- result = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop); +- +- internal_endpwent (&ent); +- +- return result; +-} +- +-/* This function handle the + entry in /etc/passwd for getpwuid */ +-static enum nss_status +-getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer, +- size_t buflen, int *errnop) +-{ +- struct passwd pwd; +- char *p; +- size_t plen; +- +- if (!nss_getpwuid_r) +- return NSS_STATUS_UNAVAIL; +- +- memset (&pwd, '\0', sizeof (struct passwd)); +- +- copy_pwd_changes (&pwd, result, NULL, 0); +- +- plen = pwd_need_buflen (&pwd); +- if (plen > buflen) +- { +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- p = buffer + (buflen - plen); +- buflen -= plen; +- +- if (nss_getpwuid_r (uid, result, buffer, buflen, errnop) == +- NSS_STATUS_SUCCESS) +- { +- copy_pwd_changes (result, &pwd, p, plen); +- give_pwd_free (&pwd); +- /* We found the entry. */ +- return NSS_STATUS_SUCCESS; +- } +- else +- { +- /* Give buffer the old len back */ +- buflen += plen; +- give_pwd_free (&pwd); +- } +- return NSS_STATUS_RETURN; +-} +- +-/* Searches in /etc/passwd and the NSS subsystem for a special user id */ +-static enum nss_status +-internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- struct parser_data *data = (void *) buffer; +- +- while (1) +- { +- fpos_t pos; +- char *p; +- int parse_res; +- +- do +- { +- /* We need at least 3 characters for one line. */ +- if (__glibc_unlikely (buflen < 3)) +- { +- erange: +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- +- fgetpos (ent->stream, &pos); +- buffer[buflen - 1] = '\xff'; +- p = fgets_unlocked (buffer, buflen, ent->stream); +- if (p == NULL && feof_unlocked (ent->stream)) +- return NSS_STATUS_NOTFOUND; +- +- if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) +- { +- erange_reset: +- fsetpos (ent->stream, &pos); +- goto erange; +- } +- +- /* Terminate the line for any case. */ +- buffer[buflen - 1] = '\0'; +- +- /* Skip leading blanks. */ +- while (isspace (*p)) +- ++p; +- } +- while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ +- /* Parse the line. If it is invalid, loop to +- get the next line of the file to parse. */ +- !(parse_res = _nss_files_parse_pwent (p, result, data, buflen, +- errnop))); +- +- if (__glibc_unlikely (parse_res == -1)) +- /* The parser ran out of space. */ +- goto erange_reset; +- +- /* This is a real entry. */ +- if (result->pw_name[0] != '+' && result->pw_name[0] != '-') +- { +- if (result->pw_uid == uid) +- return NSS_STATUS_SUCCESS; +- else +- continue; +- } +- +- /* -@netgroup */ +- if (result->pw_name[0] == '-' && result->pw_name[1] == '@' +- && result->pw_name[2] != '\0') +- { +- /* -1, because we remove first two character of pw_name. */ +- size_t len = strlen (result->pw_name) - 1; +- char buf[len]; +- enum nss_status status; +- +- memcpy (buf, &result->pw_name[2], len); +- +- status = getpwuid_plususer (uid, result, buffer, buflen, errnop); +- if (status == NSS_STATUS_SUCCESS && +- innetgr (buf, NULL, result->pw_name, NULL)) +- return NSS_STATUS_NOTFOUND; +- +- continue; +- } +- +- /* +@netgroup */ +- if (result->pw_name[0] == '+' && result->pw_name[1] == '@' +- && result->pw_name[2] != '\0') +- { +- /* -1, because we remove first two characters of pw_name. */ +- size_t len = strlen (result->pw_name) - 1; +- char buf[len]; +- enum nss_status status; +- +- memcpy (buf, &result->pw_name[2], len); +- +- status = getpwuid_plususer (uid, result, buffer, buflen, errnop); +- +- if (status == NSS_STATUS_RETURN) +- continue; +- +- if (status == NSS_STATUS_SUCCESS) +- { +- if (innetgr (buf, NULL, result->pw_name, NULL)) +- return NSS_STATUS_SUCCESS; +- } +- else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ +- return NSS_STATUS_NOTFOUND; +- else +- return status; +- +- continue; +- } +- +- /* -user */ +- if (result->pw_name[0] == '-' && result->pw_name[1] != '\0' +- && result->pw_name[1] != '@') +- { +- size_t len = strlen (result->pw_name); +- char buf[len]; +- enum nss_status status; +- +- memcpy (buf, &result->pw_name[1], len); +- +- status = getpwuid_plususer (uid, result, buffer, buflen, errnop); +- if (status == NSS_STATUS_SUCCESS && +- innetgr (buf, NULL, result->pw_name, NULL)) +- return NSS_STATUS_NOTFOUND; +- continue; +- } +- +- /* +user */ +- if (result->pw_name[0] == '+' && result->pw_name[1] != '\0' +- && result->pw_name[1] != '@') +- { +- size_t len = strlen (result->pw_name); +- char buf[len]; +- enum nss_status status; +- +- memcpy (buf, &result->pw_name[1], len); +- +- status = getpwuid_plususer (uid, result, buffer, buflen, errnop); +- +- if (status == NSS_STATUS_RETURN) +- continue; +- +- if (status == NSS_STATUS_SUCCESS) +- { +- if (strcmp (buf, result->pw_name) == 0) +- return NSS_STATUS_SUCCESS; +- } +- else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ +- return NSS_STATUS_NOTFOUND; +- else +- return status; +- +- continue; +- } +- +- /* +:... */ +- if (result->pw_name[0] == '+' && result->pw_name[1] == '\0') +- { +- enum nss_status status; +- +- status = getpwuid_plususer (uid, result, buffer, buflen, errnop); +- if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ +- break; +- else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ +- return NSS_STATUS_NOTFOUND; +- else +- return status; +- } +- } +- return NSS_STATUS_SUCCESS; +-} +- +-enum nss_status +-_nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd, +- char *buffer, size_t buflen, int *errnop) +-{ +- enum nss_status result; +- ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }, +- { NULL, NULL, 0, 0, NULL, NULL, NULL }}; +- +- __libc_lock_lock (lock); +- +- if (ni == NULL) +- init_nss_interface (); +- +- __libc_lock_unlock (lock); +- +- result = internal_setpwent (&ent, 0, 0); +- +- if (result == NSS_STATUS_SUCCESS) +- result = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop); +- +- internal_endpwent (&ent); +- +- return result; +-} +- +- +-/* Support routines for remembering -@netgroup and -user entries. +- The names are stored in a single string with `|' as separator. */ +-static void +-blacklist_store_name (const char *name, ent_t *ent) +-{ +- int namelen = strlen (name); +- char *tmp; +- +- /* first call, setup cache */ +- if (ent->blacklist.size == 0) +- { +- ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); +- ent->blacklist.data = malloc (ent->blacklist.size); +- if (ent->blacklist.data == NULL) +- return; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- ent->blacklist.current = 1; +- } +- else +- { +- if (in_blacklist (name, namelen, ent)) +- return; /* no duplicates */ +- +- if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) +- { +- ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); +- tmp = realloc (ent->blacklist.data, ent->blacklist.size); +- if (tmp == NULL) +- { +- free (ent->blacklist.data); +- ent->blacklist.size = 0; +- return; +- } +- ent->blacklist.data = tmp; +- } +- } +- +- tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); +- *tmp++ = '|'; +- *tmp = '\0'; +- ent->blacklist.current += namelen + 1; +- +- return; +-} +- +-/* Returns TRUE if ent->blacklist contains name, else FALSE. */ +-static bool_t +-in_blacklist (const char *name, int namelen, ent_t *ent) +-{ +- char buf[namelen + 3]; +- char *cp; +- +- if (ent->blacklist.data == NULL) +- return FALSE; +- +- buf[0] = '|'; +- cp = stpcpy (&buf[1], name); +- *cp++ = '|'; +- *cp = '\0'; +- return strstr (ent->blacklist.data, buf) != NULL; +-} +Index: glibc-2.26/nis/nss_compat/compat-spwd.c +=================================================================== +--- glibc-2.26.orig/nis/nss_compat/compat-spwd.c ++++ /dev/null +@@ -1,858 +0,0 @@ +-/* Copyright (C) 1996-2017 Free Software Foundation, Inc. +- This file is part of the GNU C Library. +- Contributed by Thorsten Kukuk , 1996. +- +- 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 +- +-#include "netgroup.h" +- +-static service_user *ni; +-static enum nss_status (*nss_setspent) (int stayopen); +-static enum nss_status (*nss_getspnam_r) (const char *name, struct spwd * sp, +- char *buffer, size_t buflen, +- int *errnop); +-static enum nss_status (*nss_getspent_r) (struct spwd * sp, char *buffer, +- size_t buflen, int *errnop); +-static enum nss_status (*nss_endspent) (void); +- +-/* Get the declaration of the parser function. */ +-#define ENTNAME spent +-#define STRUCTURE spwd +-#define EXTERN_PARSER +-#include +- +-/* Structure for remembering -@netgroup and -user members ... */ +-#define BLACKLIST_INITIAL_SIZE 512 +-#define BLACKLIST_INCREMENT 256 +-struct blacklist_t +-{ +- char *data; +- int current; +- int size; +-}; +- +-struct ent_t +-{ +- bool netgroup; +- bool files; +- bool first; +- enum nss_status setent_status; +- FILE *stream; +- struct blacklist_t blacklist; +- struct spwd pwd; +- struct __netgrent netgrdata; +-}; +-typedef struct ent_t ent_t; +- +-static ent_t ext_ent = { false, true, false, NSS_STATUS_SUCCESS, NULL, +- { NULL, 0, 0}, +- { NULL, NULL, 0, 0, 0, 0, 0, 0, 0}}; +- +-/* Protect global state against multiple changers. */ +-__libc_lock_define_initialized (static, lock) +- +-/* Prototypes for local functions. */ +-static void blacklist_store_name (const char *, ent_t *); +-static int in_blacklist (const char *, int, ent_t *); +- +-/* Initialize the NSS interface/functions. The calling function must +- hold the lock. */ +-static void +-init_nss_interface (void) +-{ +- if (__nss_database_lookup ("shadow_compat", "passwd_compat", +- "nis", &ni) >= 0) +- { +- nss_setspent = __nss_lookup_function (ni, "setspent"); +- nss_getspnam_r = __nss_lookup_function (ni, "getspnam_r"); +- nss_getspent_r = __nss_lookup_function (ni, "getspent_r"); +- nss_endspent = __nss_lookup_function (ni, "endspent"); +- } +-} +- +-static void +-give_spwd_free (struct spwd *pwd) +-{ +- free (pwd->sp_namp); +- free (pwd->sp_pwdp); +- +- memset (pwd, '\0', sizeof (struct spwd)); +- pwd->sp_warn = -1; +- pwd->sp_inact = -1; +- pwd->sp_expire = -1; +- pwd->sp_flag = ~0ul; +-} +- +-static int +-spwd_need_buflen (struct spwd *pwd) +-{ +- int len = 0; +- +- if (pwd->sp_pwdp != NULL) +- len += strlen (pwd->sp_pwdp) + 1; +- +- return len; +-} +- +-static void +-copy_spwd_changes (struct spwd *dest, struct spwd *src, +- char *buffer, size_t buflen) +-{ +- if (src->sp_pwdp != NULL && strlen (src->sp_pwdp)) +- { +- if (buffer == NULL) +- dest->sp_pwdp = strdup (src->sp_pwdp); +- else if (dest->sp_pwdp && +- strlen (dest->sp_pwdp) >= strlen (src->sp_pwdp)) +- strcpy (dest->sp_pwdp, src->sp_pwdp); +- else +- { +- dest->sp_pwdp = buffer; +- strcpy (dest->sp_pwdp, src->sp_pwdp); +- buffer += strlen (dest->sp_pwdp) + 1; +- buflen = buflen - (strlen (dest->sp_pwdp) + 1); +- } +- } +- if (src->sp_lstchg != 0) +- dest->sp_lstchg = src->sp_lstchg; +- if (src->sp_min != 0) +- dest->sp_min = src->sp_min; +- if (src->sp_max != 0) +- dest->sp_max = src->sp_max; +- if (src->sp_warn != -1) +- dest->sp_warn = src->sp_warn; +- if (src->sp_inact != -1) +- dest->sp_inact = src->sp_inact; +- if (src->sp_expire != -1) +- dest->sp_expire = src->sp_expire; +- if (src->sp_flag != ~0ul) +- dest->sp_flag = src->sp_flag; +-} +- +-static enum nss_status +-internal_setspent (ent_t *ent, int stayopen, int needent) +-{ +- enum nss_status status = NSS_STATUS_SUCCESS; +- +- ent->first = ent->netgroup = 0; +- ent->files = true; +- +- /* If something was left over free it. */ +- if (ent->netgroup) +- __internal_endnetgrent (&ent->netgrdata); +- +- if (ent->blacklist.data != NULL) +- { +- ent->blacklist.current = 1; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- } +- else +- ent->blacklist.current = 0; +- +- if (ent->stream == NULL) +- { +- ent->stream = fopen ("/etc/shadow", "rme"); +- +- if (ent->stream == NULL) +- status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; +- else +- /* We take care of locking ourself. */ +- __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); +- } +- else +- rewind (ent->stream); +- +- give_spwd_free (&ent->pwd); +- +- if (needent && status == NSS_STATUS_SUCCESS && nss_setspent) +- ent->setent_status = nss_setspent (stayopen); +- +- return status; +-} +- +- +-enum nss_status +-_nss_compat_setspent (int stayopen) +-{ +- enum nss_status result; +- +- __libc_lock_lock (lock); +- +- if (ni == NULL) +- init_nss_interface (); +- +- result = internal_setspent (&ext_ent, stayopen, 1); +- +- __libc_lock_unlock (lock); +- +- return result; +-} +- +- +-static enum nss_status +-internal_endspent (ent_t *ent) +-{ +- if (ent->stream != NULL) +- { +- fclose (ent->stream); +- ent->stream = NULL; +- } +- +- if (ent->netgroup) +- __internal_endnetgrent (&ent->netgrdata); +- +- ent->first = ent->netgroup = false; +- ent->files = true; +- +- if (ent->blacklist.data != NULL) +- { +- ent->blacklist.current = 1; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- } +- else +- ent->blacklist.current = 0; +- +- give_spwd_free (&ent->pwd); +- +- return NSS_STATUS_SUCCESS; +-} +- +-enum nss_status +-_nss_compat_endspent (void) +-{ +- enum nss_status result; +- +- __libc_lock_lock (lock); +- +- if (nss_endspent) +- nss_endspent (); +- +- result = internal_endspent (&ext_ent); +- +- __libc_lock_unlock (lock); +- +- return result; +-} +- +- +-static enum nss_status +-getspent_next_nss_netgr (const char *name, struct spwd *result, ent_t *ent, +- char *group, char *buffer, size_t buflen, +- int *errnop) +-{ +- char *curdomain = NULL, *host, *user, *domain, *p2; +- size_t p2len; +- +- if (!nss_getspnam_r) +- return NSS_STATUS_UNAVAIL; +- +- /* If the setpwent call failed, say so. */ +- if (ent->setent_status != NSS_STATUS_SUCCESS) +- return ent->setent_status; +- +- if (ent->first) +- { +- memset (&ent->netgrdata, 0, sizeof (struct __netgrent)); +- __internal_setnetgrent (group, &ent->netgrdata); +- ent->first = false; +- } +- +- while (1) +- { +- enum nss_status status; +- +- status = __internal_getnetgrent_r (&host, &user, &domain, +- &ent->netgrdata, buffer, buflen, +- errnop); +- if (status != 1) +- { +- __internal_endnetgrent (&ent->netgrdata); +- ent->netgroup = false; +- give_spwd_free (&ent->pwd); +- return NSS_STATUS_RETURN; +- } +- +- if (user == NULL || user[0] == '-') +- continue; +- +- if (domain != NULL) +- { +- if (curdomain == NULL +- && yp_get_default_domain (&curdomain) != YPERR_SUCCESS) +- { +- __internal_endnetgrent (&ent->netgrdata); +- ent->netgroup = false; +- give_spwd_free (&ent->pwd); +- return NSS_STATUS_UNAVAIL; +- } +- if (strcmp (curdomain, domain) != 0) +- continue; +- } +- +- /* If name != NULL, we are called from getpwnam */ +- if (name != NULL) +- if (strcmp (user, name) != 0) +- continue; +- +- p2len = spwd_need_buflen (&ent->pwd); +- if (p2len > buflen) +- { +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- p2 = buffer + (buflen - p2len); +- buflen -= p2len; +- +- if (nss_getspnam_r (user, result, buffer, buflen, errnop) != +- NSS_STATUS_SUCCESS) +- continue; +- +- if (!in_blacklist (result->sp_namp, strlen (result->sp_namp), ent)) +- { +- /* Store the User in the blacklist for possible the "+" at the +- end of /etc/passwd */ +- blacklist_store_name (result->sp_namp, ent); +- copy_spwd_changes (result, &ent->pwd, p2, p2len); +- break; +- } +- } +- +- return NSS_STATUS_SUCCESS; +-} +- +- +-static enum nss_status +-getspent_next_nss (struct spwd *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- enum nss_status status; +- char *p2; +- size_t p2len; +- +- if (!nss_getspent_r) +- return NSS_STATUS_UNAVAIL; +- +- p2len = spwd_need_buflen (&ent->pwd); +- if (p2len > buflen) +- { +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- p2 = buffer + (buflen - p2len); +- buflen -= p2len; +- do +- { +- if ((status = nss_getspent_r (result, buffer, buflen, errnop)) != +- NSS_STATUS_SUCCESS) +- return status; +- } +- while (in_blacklist (result->sp_namp, strlen (result->sp_namp), ent)); +- +- copy_spwd_changes (result, &ent->pwd, p2, p2len); +- +- return NSS_STATUS_SUCCESS; +-} +- +- +-/* This function handle the +user entrys in /etc/shadow */ +-static enum nss_status +-getspnam_plususer (const char *name, struct spwd *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- if (!nss_getspnam_r) +- return NSS_STATUS_UNAVAIL; +- +- struct spwd pwd; +- memset (&pwd, '\0', sizeof (struct spwd)); +- pwd.sp_warn = -1; +- pwd.sp_inact = -1; +- pwd.sp_expire = -1; +- pwd.sp_flag = ~0ul; +- +- copy_spwd_changes (&pwd, result, NULL, 0); +- +- size_t plen = spwd_need_buflen (&pwd); +- if (plen > buflen) +- { +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- char *p = buffer + (buflen - plen); +- buflen -= plen; +- +- enum nss_status status = nss_getspnam_r (name, result, buffer, buflen, +- errnop); +- if (status != NSS_STATUS_SUCCESS) +- return status; +- +- if (in_blacklist (result->sp_namp, strlen (result->sp_namp), ent)) +- return NSS_STATUS_NOTFOUND; +- +- copy_spwd_changes (result, &pwd, p, plen); +- give_spwd_free (&pwd); +- /* We found the entry. */ +- return NSS_STATUS_SUCCESS; +-} +- +- +-static enum nss_status +-getspent_next_file (struct spwd *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- struct parser_data *data = (void *) buffer; +- while (1) +- { +- fpos_t pos; +- int parse_res = 0; +- char *p; +- +- do +- { +- /* We need at least 3 characters for one line. */ +- if (__glibc_unlikely (buflen < 3)) +- { +- erange: +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- +- fgetpos (ent->stream, &pos); +- buffer[buflen - 1] = '\xff'; +- p = fgets_unlocked (buffer, buflen, ent->stream); +- if (p == NULL && feof_unlocked (ent->stream)) +- return NSS_STATUS_NOTFOUND; +- +- if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) +- { +- erange_reset: +- fsetpos (ent->stream, &pos); +- goto erange; +- } +- +- /* Skip leading blanks. */ +- while (isspace (*p)) +- ++p; +- } +- while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */ +- /* Parse the line. If it is invalid, loop to +- get the next line of the file to parse. */ +- || !(parse_res = _nss_files_parse_spent (p, result, data, +- buflen, errnop))); +- +- if (__glibc_unlikely (parse_res == -1)) +- /* The parser ran out of space. */ +- goto erange_reset; +- +- if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-') +- /* This is a real entry. */ +- break; +- +- /* -@netgroup */ +- if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@' +- && result->sp_namp[2] != '\0') +- { +- /* XXX Do not use fixed length buffers. */ +- char buf2[1024]; +- char *user, *host, *domain; +- struct __netgrent netgrdata; +- +- memset (&netgrdata, 0, sizeof (struct __netgrent)); +- __internal_setnetgrent (&result->sp_namp[2], &netgrdata); +- while (__internal_getnetgrent_r (&host, &user, &domain, +- &netgrdata, buf2, sizeof (buf2), +- errnop)) +- { +- if (user != NULL && user[0] != '-') +- blacklist_store_name (user, ent); +- } +- __internal_endnetgrent (&netgrdata); +- continue; +- } +- +- /* +@netgroup */ +- if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@' +- && result->sp_namp[2] != '\0') +- { +- int status; +- +- ent->netgroup = true; +- ent->first = true; +- copy_spwd_changes (&ent->pwd, result, NULL, 0); +- +- status = getspent_next_nss_netgr (NULL, result, ent, +- &result->sp_namp[2], +- buffer, buflen, errnop); +- if (status == NSS_STATUS_RETURN) +- continue; +- else +- return status; +- } +- +- /* -user */ +- if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0' +- && result->sp_namp[1] != '@') +- { +- blacklist_store_name (&result->sp_namp[1], ent); +- continue; +- } +- +- /* +user */ +- if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0' +- && result->sp_namp[1] != '@') +- { +- size_t len = strlen (result->sp_namp); +- char buf[len]; +- enum nss_status status; +- +- /* Store the User in the blacklist for the "+" at the end of +- /etc/passwd */ +- memcpy (buf, &result->sp_namp[1], len); +- status = getspnam_plususer (&result->sp_namp[1], result, ent, +- buffer, buflen, errnop); +- blacklist_store_name (buf, ent); +- +- if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ +- break; +- /* We couldn't parse the entry */ +- else if (status == NSS_STATUS_RETURN +- /* entry doesn't exist */ +- || status == NSS_STATUS_NOTFOUND) +- continue; +- else +- { +- if (status == NSS_STATUS_TRYAGAIN) +- { +- fsetpos (ent->stream, &pos); +- *errnop = ERANGE; +- } +- return status; +- } +- } +- +- /* +:... */ +- if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0') +- { +- ent->files = false; +- ent->first = true; +- copy_spwd_changes (&ent->pwd, result, NULL, 0); +- +- return getspent_next_nss (result, ent, buffer, buflen, errnop); +- } +- } +- +- return NSS_STATUS_SUCCESS; +-} +- +- +-static enum nss_status +-internal_getspent_r (struct spwd *pw, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- if (ent->netgroup) +- { +- enum nss_status status; +- +- /* We are searching members in a netgroup */ +- /* Since this is not the first call, we don't need the group name */ +- status = getspent_next_nss_netgr (NULL, pw, ent, NULL, buffer, +- buflen, errnop); +- +- if (status == NSS_STATUS_RETURN) +- return getspent_next_file (pw, ent, buffer, buflen, errnop); +- else +- return status; +- } +- else if (ent->files) +- return getspent_next_file (pw, ent, buffer, buflen, errnop); +- else +- return getspent_next_nss (pw, ent, buffer, buflen, errnop); +-} +- +- +-enum nss_status +-_nss_compat_getspent_r (struct spwd *pwd, char *buffer, size_t buflen, +- int *errnop) +-{ +- enum nss_status result = NSS_STATUS_SUCCESS; +- +- __libc_lock_lock (lock); +- +- /* Be prepared that the setpwent function was not called before. */ +- if (ni == NULL) +- init_nss_interface (); +- +- if (ext_ent.stream == NULL) +- result = internal_setspent (&ext_ent, 1, 1); +- +- if (result == NSS_STATUS_SUCCESS) +- result = internal_getspent_r (pwd, &ext_ent, buffer, buflen, errnop); +- +- __libc_lock_unlock (lock); +- +- return result; +-} +- +- +-/* Searches in /etc/passwd and the NIS/NIS+ map for a special user */ +-static enum nss_status +-internal_getspnam_r (const char *name, struct spwd *result, ent_t *ent, +- char *buffer, size_t buflen, int *errnop) +-{ +- struct parser_data *data = (void *) buffer; +- +- while (1) +- { +- fpos_t pos; +- char *p; +- int parse_res; +- +- do +- { +- /* We need at least 3 characters for one line. */ +- if (__glibc_unlikely (buflen < 3)) +- { +- erange: +- *errnop = ERANGE; +- return NSS_STATUS_TRYAGAIN; +- } +- +- fgetpos (ent->stream, &pos); +- buffer[buflen - 1] = '\xff'; +- p = fgets_unlocked (buffer, buflen, ent->stream); +- if (p == NULL && feof_unlocked (ent->stream)) +- return NSS_STATUS_NOTFOUND; +- +- if (p == NULL || buffer[buflen - 1] != '\xff') +- { +- erange_reset: +- fsetpos (ent->stream, &pos); +- goto erange; +- } +- +- /* Terminate the line for any case. */ +- buffer[buflen - 1] = '\0'; +- +- /* Skip leading blanks. */ +- while (isspace (*p)) +- ++p; +- } +- while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ +- /* Parse the line. If it is invalid, loop to +- get the next line of the file to parse. */ +- !(parse_res = _nss_files_parse_spent (p, result, data, buflen, +- errnop))); +- +- if (__glibc_unlikely (parse_res == -1)) +- /* The parser ran out of space. */ +- goto erange_reset; +- +- /* This is a real entry. */ +- if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-') +- { +- if (strcmp (result->sp_namp, name) == 0) +- return NSS_STATUS_SUCCESS; +- else +- continue; +- } +- +- /* -@netgroup */ +- /* If the loaded NSS module does not support this service, add +- all users from a +@netgroup entry to the blacklist, too. */ +- if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@' +- && result->sp_namp[2] != '\0') +- { +- if (innetgr (&result->sp_namp[2], NULL, name, NULL)) +- return NSS_STATUS_NOTFOUND; +- continue; +- } +- +- /* +@netgroup */ +- if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@' +- && result->sp_namp[2] != '\0') +- { +- enum nss_status status; +- +- if (innetgr (&result->sp_namp[2], NULL, name, NULL)) +- { +- status = getspnam_plususer (name, result, ent, buffer, +- buflen, errnop); +- +- if (status == NSS_STATUS_RETURN) +- continue; +- +- return status; +- } +- continue; +- } +- +- /* -user */ +- if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0' +- && result->sp_namp[1] != '@') +- { +- if (strcmp (&result->sp_namp[1], name) == 0) +- return NSS_STATUS_NOTFOUND; +- else +- continue; +- } +- +- /* +user */ +- if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0' +- && result->sp_namp[1] != '@') +- { +- if (strcmp (name, &result->sp_namp[1]) == 0) +- { +- enum nss_status status; +- +- status = getspnam_plususer (name, result, ent, +- buffer, buflen, errnop); +- +- if (status == NSS_STATUS_RETURN) +- /* We couldn't parse the entry */ +- return NSS_STATUS_NOTFOUND; +- else +- return status; +- } +- } +- +- /* +:... */ +- if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0') +- { +- enum nss_status status; +- +- status = getspnam_plususer (name, result, ent, +- buffer, buflen, errnop); +- +- if (status == NSS_STATUS_SUCCESS) +- /* We found the entry. */ +- break; +- else if (status == NSS_STATUS_RETURN) +- /* We couldn't parse the entry */ +- return NSS_STATUS_NOTFOUND; +- else +- return status; +- } +- } +- return NSS_STATUS_SUCCESS; +-} +- +- +-enum nss_status +-_nss_compat_getspnam_r (const char *name, struct spwd *pwd, +- char *buffer, size_t buflen, int *errnop) +-{ +- enum nss_status result; +- ent_t ent = { false, true, false, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0}, +- { NULL, NULL, 0, 0, 0, 0, 0, 0, 0}}; +- +- if (name[0] == '-' || name[0] == '+') +- return NSS_STATUS_NOTFOUND; +- +- __libc_lock_lock (lock); +- +- if (ni == NULL) +- init_nss_interface (); +- +- __libc_lock_unlock (lock); +- +- result = internal_setspent (&ent, 0, 0); +- +- if (result == NSS_STATUS_SUCCESS) +- result = internal_getspnam_r (name, pwd, &ent, buffer, buflen, errnop); +- +- internal_endspent (&ent); +- +- return result; +-} +- +- +-/* Support routines for remembering -@netgroup and -user entries. +- The names are stored in a single string with `|' as separator. */ +-static void +-blacklist_store_name (const char *name, ent_t *ent) +-{ +- int namelen = strlen (name); +- char *tmp; +- +- /* first call, setup cache */ +- if (ent->blacklist.size == 0) +- { +- ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); +- ent->blacklist.data = malloc (ent->blacklist.size); +- if (ent->blacklist.data == NULL) +- return; +- ent->blacklist.data[0] = '|'; +- ent->blacklist.data[1] = '\0'; +- ent->blacklist.current = 1; +- } +- else +- { +- if (in_blacklist (name, namelen, ent)) +- return; /* no duplicates */ +- +- if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) +- { +- ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); +- tmp = realloc (ent->blacklist.data, ent->blacklist.size); +- if (tmp == NULL) +- { +- free (ent->blacklist.data); +- ent->blacklist.size = 0; +- return; +- } +- ent->blacklist.data = tmp; +- } +- } +- +- tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); +- *tmp++ = '|'; +- *tmp = '\0'; +- ent->blacklist.current += namelen + 1; +- +- return; +-} +- +- +-/* Returns TRUE if ent->blacklist contains name, else FALSE. */ +-static bool_t +-in_blacklist (const char *name, int namelen, ent_t *ent) +-{ +- char buf[namelen + 3]; +- char *cp; +- +- if (ent->blacklist.data == NULL) +- return false; +- +- buf[0] = '|'; +- cp = stpcpy (&buf[1], name); +- *cp++ = '|'; +- *cp = '\0'; +- return strstr (ent->blacklist.data, buf) != NULL; +-} +Index: glibc-2.26/nss/Makefile +=================================================================== +--- glibc-2.26.orig/nss/Makefile ++++ glibc-2.26/nss/Makefile +@@ -70,7 +70,7 @@ tests += tst-cancel-getpwuid_r + endif + + # Specify rules for the nss_* modules. We have some services. +-services := files db ++services := files db compat + + extra-libs = $(services:%=libnss_%) + # These libraries will be built in the `others' pass rather than +@@ -93,11 +93,15 @@ libnss_db-routines := $(libnss_db-dbs) d + generated += $(filter-out db-alias.c db-netgrp.c, \ + $(addsuffix .c,$(libnss_db-dbs))) + ++libnss_compat-routines := $(addprefix compat-,grp pwd spwd initgroups) \ ++ nisdomain ++ + install-others += $(inst_vardbdir)/Makefile + + # Build static module into libc if requested + libnss_files-inhibit-o = $(filter-out .os,$(object-suffixes)) + libnss_db-inhibit-o = $(filter-out .os,$(object-suffixes)) ++libnss_compat-inhibit-o = $(filter-out .os,$(object-suffixes)) + ifeq ($(build-static-nss),yes) + routines += $(libnss_files-routines) + static-only-routines += $(libnss_files-routines) +Index: glibc-2.26/nss/Versions +=================================================================== +--- glibc-2.26.orig/nss/Versions ++++ glibc-2.26/nss/Versions +@@ -160,3 +160,14 @@ libnss_db { + _nss_db_init; + } + } ++ ++libnss_compat { ++ GLIBC_PRIVATE { ++ _nss_compat_endgrent; _nss_compat_endpwent; _nss_compat_endspent; ++ _nss_compat_getgrent_r; _nss_compat_getgrgid_r; _nss_compat_getgrnam_r; ++ _nss_compat_getpwent_r; _nss_compat_getpwnam_r; _nss_compat_getpwuid_r; ++ _nss_compat_getspent_r; _nss_compat_getspnam_r; ++ _nss_compat_setgrent; _nss_compat_setpwent; _nss_compat_setspent; ++ _nss_compat_initgroups_dyn; ++ } ++} +Index: glibc-2.26/nss/nss_compat/compat-grp.c +=================================================================== +--- /dev/null ++++ glibc-2.26/nss/nss_compat/compat-grp.c +@@ -0,0 +1,682 @@ ++/* Copyright (C) 1996-2017 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ Contributed by Thorsten Kukuk , 1996. ++ ++ 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 ++ ++static service_user *ni; ++static enum nss_status (*nss_setgrent) (int stayopen); ++static enum nss_status (*nss_getgrnam_r) (const char *name, ++ struct group * grp, char *buffer, ++ size_t buflen, int *errnop); ++static enum nss_status (*nss_getgrgid_r) (gid_t gid, struct group * grp, ++ char *buffer, size_t buflen, ++ int *errnop); ++static enum nss_status (*nss_getgrent_r) (struct group * grp, char *buffer, ++ size_t buflen, int *errnop); ++static enum nss_status (*nss_endgrent) (void); ++ ++/* Get the declaration of the parser function. */ ++#define ENTNAME grent ++#define STRUCTURE group ++#define EXTERN_PARSER ++#include ++ ++/* Structure for remembering -group members ... */ ++#define BLACKLIST_INITIAL_SIZE 512 ++#define BLACKLIST_INCREMENT 256 ++struct blacklist_t ++{ ++ char *data; ++ int current; ++ int size; ++}; ++ ++struct ent_t ++{ ++ bool files; ++ enum nss_status setent_status; ++ FILE *stream; ++ struct blacklist_t blacklist; ++}; ++typedef struct ent_t ent_t; ++ ++static ent_t ext_ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; ++ ++/* Protect global state against multiple changers. */ ++__libc_lock_define_initialized (static, lock) ++ ++/* Prototypes for local functions. */ ++static void blacklist_store_name (const char *, ent_t *); ++static bool in_blacklist (const char *, int, ent_t *); ++ ++/* Initialize the NSS interface/functions. The calling function must ++ hold the lock. */ ++static void ++init_nss_interface (void) ++{ ++ if (__nss_database_lookup ("group_compat", NULL, "nis", &ni) >= 0) ++ { ++ nss_setgrent = __nss_lookup_function (ni, "setgrent"); ++ nss_getgrnam_r = __nss_lookup_function (ni, "getgrnam_r"); ++ nss_getgrgid_r = __nss_lookup_function (ni, "getgrgid_r"); ++ nss_getgrent_r = __nss_lookup_function (ni, "getgrent_r"); ++ nss_endgrent = __nss_lookup_function (ni, "endgrent"); ++ } ++} ++ ++static enum nss_status ++internal_setgrent (ent_t *ent, int stayopen, int needent) ++{ ++ enum nss_status status = NSS_STATUS_SUCCESS; ++ ++ ent->files = true; ++ ++ if (ent->blacklist.data != NULL) ++ { ++ ent->blacklist.current = 1; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ } ++ else ++ ent->blacklist.current = 0; ++ ++ if (ent->stream == NULL) ++ { ++ ent->stream = fopen ("/etc/group", "rme"); ++ ++ if (ent->stream == NULL) ++ status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; ++ else ++ /* We take care of locking ourself. */ ++ __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); ++ } ++ else ++ rewind (ent->stream); ++ ++ if (needent && status == NSS_STATUS_SUCCESS && nss_setgrent) ++ ent->setent_status = nss_setgrent (stayopen); ++ ++ return status; ++} ++ ++ ++enum nss_status ++_nss_compat_setgrent (int stayopen) ++{ ++ enum nss_status result; ++ ++ __libc_lock_lock (lock); ++ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ result = internal_setgrent (&ext_ent, stayopen, 1); ++ ++ __libc_lock_unlock (lock); ++ ++ return result; ++} ++ ++ ++static enum nss_status ++internal_endgrent (ent_t *ent) ++{ ++ if (ent->stream != NULL) ++ { ++ fclose (ent->stream); ++ ent->stream = NULL; ++ } ++ ++ if (ent->blacklist.data != NULL) ++ { ++ ent->blacklist.current = 1; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ } ++ else ++ ent->blacklist.current = 0; ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++enum nss_status ++_nss_compat_endgrent (void) ++{ ++ enum nss_status result; ++ ++ __libc_lock_lock (lock); ++ ++ if (nss_endgrent) ++ nss_endgrent (); ++ ++ result = internal_endgrent (&ext_ent); ++ ++ __libc_lock_unlock (lock); ++ ++ return result; ++} ++ ++/* get the next group from NSS (+ entry) */ ++static enum nss_status ++getgrent_next_nss (struct group *result, ent_t *ent, char *buffer, ++ size_t buflen, int *errnop) ++{ ++ if (!nss_getgrent_r) ++ return NSS_STATUS_UNAVAIL; ++ ++ /* If the setgrent call failed, say so. */ ++ if (ent->setent_status != NSS_STATUS_SUCCESS) ++ return ent->setent_status; ++ ++ do ++ { ++ enum nss_status status; ++ ++ if ((status = nss_getgrent_r (result, buffer, buflen, errnop)) != ++ NSS_STATUS_SUCCESS) ++ return status; ++ } ++ while (in_blacklist (result->gr_name, strlen (result->gr_name), ent)); ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++/* This function handle the +group entrys in /etc/group */ ++static enum nss_status ++getgrnam_plusgroup (const char *name, struct group *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ if (!nss_getgrnam_r) ++ return NSS_STATUS_UNAVAIL; ++ ++ enum nss_status status = nss_getgrnam_r (name, result, buffer, buflen, ++ errnop); ++ if (status != NSS_STATUS_SUCCESS) ++ return status; ++ ++ if (in_blacklist (result->gr_name, strlen (result->gr_name), ent)) ++ return NSS_STATUS_NOTFOUND; ++ ++ /* We found the entry. */ ++ return NSS_STATUS_SUCCESS; ++} ++ ++static enum nss_status ++getgrent_next_file (struct group *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ struct parser_data *data = (void *) buffer; ++ while (1) ++ { ++ fpos_t pos; ++ int parse_res = 0; ++ char *p; ++ ++ do ++ { ++ /* We need at least 3 characters for one line. */ ++ if (__glibc_unlikely (buflen < 3)) ++ { ++ erange: ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ ++ fgetpos (ent->stream, &pos); ++ buffer[buflen - 1] = '\xff'; ++ p = fgets_unlocked (buffer, buflen, ent->stream); ++ if (p == NULL && feof_unlocked (ent->stream)) ++ return NSS_STATUS_NOTFOUND; ++ ++ if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) ++ { ++ erange_reset: ++ fsetpos (ent->stream, &pos); ++ goto erange; ++ } ++ ++ /* Terminate the line for any case. */ ++ buffer[buflen - 1] = '\0'; ++ ++ /* Skip leading blanks. */ ++ while (isspace (*p)) ++ ++p; ++ } ++ while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ ++ /* Parse the line. If it is invalid, loop to ++ get the next line of the file to parse. */ ++ !(parse_res = _nss_files_parse_grent (p, result, data, buflen, ++ errnop))); ++ ++ if (__glibc_unlikely (parse_res == -1)) ++ /* The parser ran out of space. */ ++ goto erange_reset; ++ ++ if (result->gr_name[0] != '+' && result->gr_name[0] != '-') ++ /* This is a real entry. */ ++ break; ++ ++ /* -group */ ++ if (result->gr_name[0] == '-' && result->gr_name[1] != '\0' ++ && result->gr_name[1] != '@') ++ { ++ blacklist_store_name (&result->gr_name[1], ent); ++ continue; ++ } ++ ++ /* +group */ ++ if (result->gr_name[0] == '+' && result->gr_name[1] != '\0' ++ && result->gr_name[1] != '@') ++ { ++ size_t len = strlen (result->gr_name); ++ char buf[len]; ++ enum nss_status status; ++ ++ /* Store the group in the blacklist for the "+" at the end of ++ /etc/group */ ++ memcpy (buf, &result->gr_name[1], len); ++ status = getgrnam_plusgroup (&result->gr_name[1], result, ent, ++ buffer, buflen, errnop); ++ blacklist_store_name (buf, ent); ++ if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ ++ break; ++ else if (status == NSS_STATUS_RETURN /* We couldn't parse the entry*/ ++ || status == NSS_STATUS_NOTFOUND) /* No group in NIS */ ++ continue; ++ else ++ { ++ if (status == NSS_STATUS_TRYAGAIN) ++ /* The parser ran out of space. */ ++ goto erange_reset; ++ ++ return status; ++ } ++ } ++ ++ /* +:... */ ++ if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') ++ { ++ ent->files = false; ++ ++ return getgrent_next_nss (result, ent, buffer, buflen, errnop); ++ } ++ } ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++ ++enum nss_status ++_nss_compat_getgrent_r (struct group *grp, char *buffer, size_t buflen, ++ int *errnop) ++{ ++ enum nss_status result = NSS_STATUS_SUCCESS; ++ ++ __libc_lock_lock (lock); ++ ++ /* Be prepared that the setgrent function was not called before. */ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ if (ext_ent.stream == NULL) ++ result = internal_setgrent (&ext_ent, 1, 1); ++ ++ if (result == NSS_STATUS_SUCCESS) ++ { ++ if (ext_ent.files) ++ result = getgrent_next_file (grp, &ext_ent, buffer, buflen, errnop); ++ else ++ result = getgrent_next_nss (grp, &ext_ent, buffer, buflen, errnop); ++ } ++ __libc_lock_unlock (lock); ++ ++ return result; ++} ++ ++/* Searches in /etc/group and the NIS/NIS+ map for a special group */ ++static enum nss_status ++internal_getgrnam_r (const char *name, struct group *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ struct parser_data *data = (void *) buffer; ++ while (1) ++ { ++ fpos_t pos; ++ int parse_res = 0; ++ char *p; ++ ++ do ++ { ++ /* We need at least 3 characters for one line. */ ++ if (__glibc_unlikely (buflen < 3)) ++ { ++ erange: ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ ++ fgetpos (ent->stream, &pos); ++ buffer[buflen - 1] = '\xff'; ++ p = fgets_unlocked (buffer, buflen, ent->stream); ++ if (p == NULL && feof_unlocked (ent->stream)) ++ return NSS_STATUS_NOTFOUND; ++ ++ if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) ++ { ++ erange_reset: ++ fsetpos (ent->stream, &pos); ++ goto erange; ++ } ++ ++ /* Terminate the line for any case. */ ++ buffer[buflen - 1] = '\0'; ++ ++ /* Skip leading blanks. */ ++ while (isspace (*p)) ++ ++p; ++ } ++ while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ ++ /* Parse the line. If it is invalid, loop to ++ get the next line of the file to parse. */ ++ !(parse_res = _nss_files_parse_grent (p, result, data, buflen, ++ errnop))); ++ ++ if (__glibc_unlikely (parse_res == -1)) ++ /* The parser ran out of space. */ ++ goto erange_reset; ++ ++ /* This is a real entry. */ ++ if (result->gr_name[0] != '+' && result->gr_name[0] != '-') ++ { ++ if (strcmp (result->gr_name, name) == 0) ++ return NSS_STATUS_SUCCESS; ++ else ++ continue; ++ } ++ ++ /* -group */ ++ if (result->gr_name[0] == '-' && result->gr_name[1] != '\0') ++ { ++ if (strcmp (&result->gr_name[1], name) == 0) ++ return NSS_STATUS_NOTFOUND; ++ else ++ continue; ++ } ++ ++ /* +group */ ++ if (result->gr_name[0] == '+' && result->gr_name[1] != '\0') ++ { ++ if (strcmp (name, &result->gr_name[1]) == 0) ++ { ++ enum nss_status status; ++ ++ status = getgrnam_plusgroup (name, result, ent, ++ buffer, buflen, errnop); ++ if (status == NSS_STATUS_RETURN) ++ /* We couldn't parse the entry */ ++ continue; ++ else ++ return status; ++ } ++ } ++ /* +:... */ ++ if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') ++ { ++ enum nss_status status; ++ ++ status = getgrnam_plusgroup (name, result, ent, ++ buffer, buflen, errnop); ++ if (status == NSS_STATUS_RETURN) ++ /* We couldn't parse the entry */ ++ continue; ++ else ++ return status; ++ } ++ } ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++enum nss_status ++_nss_compat_getgrnam_r (const char *name, struct group *grp, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ ent_t ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; ++ enum nss_status result; ++ ++ if (name[0] == '-' || name[0] == '+') ++ return NSS_STATUS_NOTFOUND; ++ ++ __libc_lock_lock (lock); ++ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ __libc_lock_unlock (lock); ++ ++ result = internal_setgrent (&ent, 0, 0); ++ ++ if (result == NSS_STATUS_SUCCESS) ++ result = internal_getgrnam_r (name, grp, &ent, buffer, buflen, errnop); ++ ++ internal_endgrent (&ent); ++ ++ return result; ++} ++ ++/* Searches in /etc/group and the NIS/NIS+ map for a special group id */ ++static enum nss_status ++internal_getgrgid_r (gid_t gid, struct group *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ struct parser_data *data = (void *) buffer; ++ while (1) ++ { ++ fpos_t pos; ++ int parse_res = 0; ++ char *p; ++ ++ do ++ { ++ /* We need at least 3 characters for one line. */ ++ if (__glibc_unlikely (buflen < 3)) ++ { ++ erange: ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ ++ fgetpos (ent->stream, &pos); ++ buffer[buflen - 1] = '\xff'; ++ p = fgets_unlocked (buffer, buflen, ent->stream); ++ if (p == NULL && feof_unlocked (ent->stream)) ++ return NSS_STATUS_NOTFOUND; ++ ++ if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) ++ { ++ erange_reset: ++ fsetpos (ent->stream, &pos); ++ goto erange; ++ } ++ ++ /* Terminate the line for any case. */ ++ buffer[buflen - 1] = '\0'; ++ ++ /* Skip leading blanks. */ ++ while (isspace (*p)) ++ ++p; ++ } ++ while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ ++ /* Parse the line. If it is invalid, loop to ++ get the next line of the file to parse. */ ++ !(parse_res = _nss_files_parse_grent (p, result, data, buflen, ++ errnop))); ++ ++ if (__glibc_unlikely (parse_res == -1)) ++ /* The parser ran out of space. */ ++ goto erange_reset; ++ ++ /* This is a real entry. */ ++ if (result->gr_name[0] != '+' && result->gr_name[0] != '-') ++ { ++ if (result->gr_gid == gid) ++ return NSS_STATUS_SUCCESS; ++ else ++ continue; ++ } ++ ++ /* -group */ ++ if (result->gr_name[0] == '-' && result->gr_name[1] != '\0') ++ { ++ blacklist_store_name (&result->gr_name[1], ent); ++ continue; ++ } ++ ++ /* +group */ ++ if (result->gr_name[0] == '+' && result->gr_name[1] != '\0') ++ { ++ /* Yes, no +1, see the memcpy call below. */ ++ size_t len = strlen (result->gr_name); ++ char buf[len]; ++ enum nss_status status; ++ ++ /* Store the group in the blacklist for the "+" at the end of ++ /etc/group */ ++ memcpy (buf, &result->gr_name[1], len); ++ status = getgrnam_plusgroup (&result->gr_name[1], result, ent, ++ buffer, buflen, errnop); ++ blacklist_store_name (buf, ent); ++ if (status == NSS_STATUS_SUCCESS && result->gr_gid == gid) ++ break; ++ else ++ continue; ++ } ++ /* +:... */ ++ if (result->gr_name[0] == '+' && result->gr_name[1] == '\0') ++ { ++ if (!nss_getgrgid_r) ++ return NSS_STATUS_UNAVAIL; ++ ++ enum nss_status status = nss_getgrgid_r (gid, result, buffer, buflen, ++ errnop); ++ if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ ++ return NSS_STATUS_NOTFOUND; ++ else ++ return status; ++ } ++ } ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++enum nss_status ++_nss_compat_getgrgid_r (gid_t gid, struct group *grp, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ ent_t ent = { true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }}; ++ enum nss_status result; ++ ++ __libc_lock_lock (lock); ++ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ __libc_lock_unlock (lock); ++ ++ result = internal_setgrent (&ent, 0, 0); ++ ++ if (result == NSS_STATUS_SUCCESS) ++ result = internal_getgrgid_r (gid, grp, &ent, buffer, buflen, errnop); ++ ++ internal_endgrent (&ent); ++ ++ return result; ++} ++ ++ ++/* Support routines for remembering -@netgroup and -user entries. ++ The names are stored in a single string with `|' as separator. */ ++static void ++blacklist_store_name (const char *name, ent_t *ent) ++{ ++ int namelen = strlen (name); ++ char *tmp; ++ ++ /* first call, setup cache */ ++ if (ent->blacklist.size == 0) ++ { ++ ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); ++ ent->blacklist.data = malloc (ent->blacklist.size); ++ if (ent->blacklist.data == NULL) ++ return; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ ent->blacklist.current = 1; ++ } ++ else ++ { ++ if (in_blacklist (name, namelen, ent)) ++ return; /* no duplicates */ ++ ++ if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) ++ { ++ ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); ++ tmp = realloc (ent->blacklist.data, ent->blacklist.size); ++ if (tmp == NULL) ++ { ++ free (ent->blacklist.data); ++ ent->blacklist.size = 0; ++ return; ++ } ++ ent->blacklist.data = tmp; ++ } ++ } ++ ++ tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); ++ *tmp++ = '|'; ++ *tmp = '\0'; ++ ent->blacklist.current += namelen + 1; ++ ++ return; ++} ++ ++/* Return whether ent->blacklist contains name. */ ++static bool ++in_blacklist (const char *name, int namelen, ent_t *ent) ++{ ++ char buf[namelen + 3]; ++ char *cp; ++ ++ if (ent->blacklist.data == NULL) ++ return false; ++ ++ buf[0] = '|'; ++ cp = stpcpy (&buf[1], name); ++ *cp++ = '|'; ++ *cp = '\0'; ++ return strstr (ent->blacklist.data, buf) != NULL; ++} +Index: glibc-2.26/nss/nss_compat/compat-initgroups.c +=================================================================== +--- /dev/null ++++ glibc-2.26/nss/nss_compat/compat-initgroups.c +@@ -0,0 +1,575 @@ ++/* Copyright (C) 1998-2017 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ Contributed by Thorsten Kukuk , 1998. ++ ++ 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 ++ ++static service_user *ni; ++/* Type of the lookup function. */ ++static enum nss_status (*nss_initgroups_dyn) (const char *, gid_t, ++ long int *, long int *, ++ gid_t **, long int, int *); ++static enum nss_status (*nss_getgrnam_r) (const char *name, ++ struct group * grp, char *buffer, ++ size_t buflen, int *errnop); ++static enum nss_status (*nss_getgrgid_r) (gid_t gid, struct group * grp, ++ char *buffer, size_t buflen, ++ int *errnop); ++static enum nss_status (*nss_setgrent) (int stayopen); ++static enum nss_status (*nss_getgrent_r) (struct group * grp, char *buffer, ++ size_t buflen, int *errnop); ++static enum nss_status (*nss_endgrent) (void); ++ ++/* Protect global state against multiple changers. */ ++__libc_lock_define_initialized (static, lock) ++ ++ ++/* Get the declaration of the parser function. */ ++#define ENTNAME grent ++#define STRUCTURE group ++#define EXTERN_PARSER ++#include ++ ++/* Structure for remembering -group members ... */ ++#define BLACKLIST_INITIAL_SIZE 512 ++#define BLACKLIST_INCREMENT 256 ++struct blacklist_t ++{ ++ char *data; ++ int current; ++ int size; ++}; ++ ++struct ent_t ++{ ++ bool files; ++ bool need_endgrent; ++ bool skip_initgroups_dyn; ++ FILE *stream; ++ struct blacklist_t blacklist; ++}; ++typedef struct ent_t ent_t; ++ ++/* Prototypes for local functions. */ ++static void blacklist_store_name (const char *, ent_t *); ++static bool in_blacklist (const char *, int, ent_t *); ++ ++/* Initialize the NSS interface/functions. The calling function must ++ hold the lock. */ ++static void ++init_nss_interface (void) ++{ ++ __libc_lock_lock (lock); ++ ++ /* Retest. */ ++ if (ni == NULL ++ && __nss_database_lookup ("group_compat", NULL, "nis", &ni) >= 0) ++ { ++ nss_initgroups_dyn = __nss_lookup_function (ni, "initgroups_dyn"); ++ nss_getgrnam_r = __nss_lookup_function (ni, "getgrnam_r"); ++ nss_getgrgid_r = __nss_lookup_function (ni, "getgrgid_r"); ++ nss_setgrent = __nss_lookup_function (ni, "setgrent"); ++ nss_getgrent_r = __nss_lookup_function (ni, "getgrent_r"); ++ nss_endgrent = __nss_lookup_function (ni, "endgrent"); ++ } ++ ++ __libc_lock_unlock (lock); ++} ++ ++static enum nss_status ++internal_setgrent (ent_t *ent) ++{ ++ enum nss_status status = NSS_STATUS_SUCCESS; ++ ++ ent->files = true; ++ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ if (ent->blacklist.data != NULL) ++ { ++ ent->blacklist.current = 1; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ } ++ else ++ ent->blacklist.current = 0; ++ ++ ent->stream = fopen ("/etc/group", "rme"); ++ ++ if (ent->stream == NULL) ++ status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; ++ else ++ /* We take care of locking ourself. */ ++ __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); ++ ++ return status; ++} ++ ++ ++static enum nss_status ++internal_endgrent (ent_t *ent) ++{ ++ if (ent->stream != NULL) ++ { ++ fclose (ent->stream); ++ ent->stream = NULL; ++ } ++ ++ if (ent->blacklist.data != NULL) ++ { ++ ent->blacklist.current = 1; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ } ++ else ++ ent->blacklist.current = 0; ++ ++ if (ent->need_endgrent && nss_endgrent != NULL) ++ nss_endgrent (); ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++/* Add new group record. */ ++static void ++add_group (long int *start, long int *size, gid_t **groupsp, long int limit, ++ gid_t gid) ++{ ++ gid_t *groups = *groupsp; ++ ++ /* Matches user. 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. */ ++ return; ++ ++ if (limit <= 0) ++ newsize = 2 * *size; ++ else ++ newsize = MIN (limit, 2 * *size); ++ ++ newgroups = realloc (groups, newsize * sizeof (*groups)); ++ if (newgroups == NULL) ++ return; ++ *groupsp = groups = newgroups; ++ *size = newsize; ++ } ++ ++ groups[*start] = gid; ++ *start += 1; ++} ++ ++/* This function checks, if the user is a member of this group and if ++ yes, add the group id to the list. Return nonzero is we couldn't ++ handle the group because the user is not in the member list. */ ++static int ++check_and_add_group (const char *user, gid_t group, long int *start, ++ long int *size, gid_t **groupsp, long int limit, ++ struct group *grp) ++{ ++ char **member; ++ ++ /* Don't add main group to list of groups. */ ++ if (grp->gr_gid == group) ++ return 0; ++ ++ for (member = grp->gr_mem; *member != NULL; ++member) ++ if (strcmp (*member, user) == 0) ++ { ++ add_group (start, size, groupsp, limit, grp->gr_gid); ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/* Get the next group from NSS (+ entry). If the NSS module supports ++ initgroups_dyn, get all entries at once. */ ++static enum nss_status ++getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user, ++ gid_t group, long int *start, long int *size, ++ gid_t **groupsp, long int limit, int *errnop) ++{ ++ enum nss_status status; ++ struct group grpbuf; ++ ++ /* Try nss_initgroups_dyn if supported. We also need getgrgid_r. ++ If this function is not supported, step through the whole group ++ database with getgrent_r. */ ++ if (! ent->skip_initgroups_dyn) ++ { ++ long int mystart = 0; ++ long int mysize = limit <= 0 ? *size : limit; ++ gid_t *mygroups = malloc (mysize * sizeof (gid_t)); ++ ++ if (mygroups == NULL) ++ return NSS_STATUS_TRYAGAIN; ++ ++ /* For every gid in the list we get from the NSS module, ++ get the whole group entry. We need to do this, since we ++ need the group name to check if it is in the blacklist. ++ In worst case, this is as twice as slow as stepping with ++ getgrent_r through the whole group database. But for large ++ group databases this is faster, since the user can only be ++ in a limited number of groups. */ ++ if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups, ++ limit, errnop) == NSS_STATUS_SUCCESS) ++ { ++ status = NSS_STATUS_NOTFOUND; ++ ++ /* If there is no blacklist we can trust the underlying ++ initgroups implementation. */ ++ if (ent->blacklist.current <= 1) ++ for (int i = 0; i < mystart; i++) ++ add_group (start, size, groupsp, limit, mygroups[i]); ++ else ++ { ++ /* A temporary buffer. We use the normal buffer, until we find ++ an entry, for which this buffer is to small. In this case, we ++ overwrite the pointer with one to a bigger buffer. */ ++ char *tmpbuf = buffer; ++ size_t tmplen = buflen; ++ bool use_malloc = false; ++ ++ for (int i = 0; i < mystart; i++) ++ { ++ while ((status = nss_getgrgid_r (mygroups[i], &grpbuf, ++ tmpbuf, tmplen, errnop)) ++ == NSS_STATUS_TRYAGAIN ++ && *errnop == ERANGE) ++ { ++ if (__libc_use_alloca (tmplen * 2)) ++ { ++ if (tmpbuf == buffer) ++ { ++ tmplen *= 2; ++ tmpbuf = __alloca (tmplen); ++ } ++ else ++ tmpbuf = extend_alloca (tmpbuf, tmplen, tmplen * 2); ++ } ++ else ++ { ++ tmplen *= 2; ++ char *newbuf = realloc (use_malloc ? tmpbuf : NULL, tmplen); ++ ++ if (newbuf == NULL) ++ { ++ status = NSS_STATUS_TRYAGAIN; ++ goto done; ++ } ++ use_malloc = true; ++ tmpbuf = newbuf; ++ } ++ } ++ ++ if (__builtin_expect (status != NSS_STATUS_NOTFOUND, 1)) ++ { ++ if (__builtin_expect (status != NSS_STATUS_SUCCESS, 0)) ++ goto done; ++ ++ if (!in_blacklist (grpbuf.gr_name, ++ strlen (grpbuf.gr_name), ent) ++ && check_and_add_group (user, group, start, size, ++ groupsp, limit, &grpbuf)) ++ { ++ if (nss_setgrent != NULL) ++ { ++ nss_setgrent (1); ++ ent->need_endgrent = true; ++ } ++ ent->skip_initgroups_dyn = true; ++ ++ goto iter; ++ } ++ } ++ } ++ ++ status = NSS_STATUS_NOTFOUND; ++ ++ done: ++ if (use_malloc) ++ free (tmpbuf); ++ } ++ ++ free (mygroups); ++ ++ return status; ++ } ++ ++ free (mygroups); ++ } ++ ++ /* If we come here, the NSS module does not support initgroups_dyn ++ or we were confronted with a split group. In these cases we have ++ to step through the whole list ourself. */ ++ iter: ++ do ++ { ++ if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) != ++ NSS_STATUS_SUCCESS) ++ break; ++ } ++ while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent)); ++ ++ if (status == NSS_STATUS_SUCCESS) ++ check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf); ++ ++ return status; ++} ++ ++static enum nss_status ++internal_getgrent_r (ent_t *ent, char *buffer, size_t buflen, const char *user, ++ gid_t group, long int *start, long int *size, ++ gid_t **groupsp, long int limit, int *errnop) ++{ ++ struct parser_data *data = (void *) buffer; ++ struct group grpbuf; ++ ++ if (!ent->files) ++ return getgrent_next_nss (ent, buffer, buflen, user, group, ++ start, size, groupsp, limit, errnop); ++ ++ while (1) ++ { ++ fpos_t pos; ++ int parse_res = 0; ++ char *p; ++ ++ do ++ { ++ /* We need at least 3 characters for one line. */ ++ if (__glibc_unlikely (buflen < 3)) ++ { ++ erange: ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ ++ fgetpos (ent->stream, &pos); ++ buffer[buflen - 1] = '\xff'; ++ p = fgets_unlocked (buffer, buflen, ent->stream); ++ if (p == NULL && feof_unlocked (ent->stream)) ++ return NSS_STATUS_NOTFOUND; ++ ++ if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) ++ { ++ erange_reset: ++ fsetpos (ent->stream, &pos); ++ goto erange; ++ } ++ ++ /* Terminate the line for any case. */ ++ buffer[buflen - 1] = '\0'; ++ ++ /* Skip leading blanks. */ ++ while (isspace (*p)) ++ ++p; ++ } ++ while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ ++ /* Parse the line. If it is invalid, loop to ++ get the next line of the file to parse. */ ++ !(parse_res = _nss_files_parse_grent (p, &grpbuf, data, buflen, ++ errnop))); ++ ++ if (__glibc_unlikely (parse_res == -1)) ++ /* The parser ran out of space. */ ++ goto erange_reset; ++ ++ if (grpbuf.gr_name[0] != '+' && grpbuf.gr_name[0] != '-') ++ /* This is a real entry. */ ++ break; ++ ++ /* -group */ ++ if (grpbuf.gr_name[0] == '-' && grpbuf.gr_name[1] != '\0' ++ && grpbuf.gr_name[1] != '@') ++ { ++ blacklist_store_name (&grpbuf.gr_name[1], ent); ++ continue; ++ } ++ ++ /* +group */ ++ if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] != '\0' ++ && grpbuf.gr_name[1] != '@') ++ { ++ if (in_blacklist (&grpbuf.gr_name[1], ++ strlen (&grpbuf.gr_name[1]), ent)) ++ continue; ++ /* Store the group in the blacklist for the "+" at the end of ++ /etc/group */ ++ blacklist_store_name (&grpbuf.gr_name[1], ent); ++ if (nss_getgrnam_r == NULL) ++ return NSS_STATUS_UNAVAIL; ++ else if (nss_getgrnam_r (&grpbuf.gr_name[1], &grpbuf, buffer, ++ buflen, errnop) != NSS_STATUS_SUCCESS) ++ continue; ++ ++ check_and_add_group (user, group, start, size, groupsp, ++ limit, &grpbuf); ++ ++ return NSS_STATUS_SUCCESS; ++ } ++ ++ /* +:... */ ++ if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] == '\0') ++ { ++ /* If the selected module does not support getgrent_r or ++ initgroups_dyn, abort. We cannot find the needed group ++ entries. */ ++ if (nss_initgroups_dyn == NULL || nss_getgrgid_r == NULL) ++ { ++ if (nss_setgrent != NULL) ++ { ++ nss_setgrent (1); ++ ent->need_endgrent = true; ++ } ++ ent->skip_initgroups_dyn = true; ++ ++ if (nss_getgrent_r == NULL) ++ return NSS_STATUS_UNAVAIL; ++ } ++ ++ ent->files = false; ++ ++ return getgrent_next_nss (ent, buffer, buflen, user, group, ++ start, size, groupsp, limit, errnop); ++ } ++ } ++ ++ check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf); ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++ ++enum nss_status ++_nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start, ++ long int *size, gid_t **groupsp, long int limit, ++ int *errnop) ++{ ++ enum nss_status status; ++ ent_t intern = { true, false, false, NULL, {NULL, 0, 0} }; ++ ++ status = internal_setgrent (&intern); ++ if (status != NSS_STATUS_SUCCESS) ++ return status; ++ ++ struct scratch_buffer tmpbuf; ++ scratch_buffer_init (&tmpbuf); ++ ++ do ++ { ++ while ((status = internal_getgrent_r (&intern, tmpbuf.data, tmpbuf.length, ++ user, group, start, size, ++ groupsp, limit, errnop)) ++ == NSS_STATUS_TRYAGAIN && *errnop == ERANGE) ++ if (!scratch_buffer_grow (&tmpbuf)) ++ goto done; ++ } ++ while (status == NSS_STATUS_SUCCESS); ++ ++ status = NSS_STATUS_SUCCESS; ++ ++ done: ++ scratch_buffer_free (&tmpbuf); ++ ++ internal_endgrent (&intern); ++ ++ return status; ++} ++ ++ ++/* Support routines for remembering -@netgroup and -user entries. ++ The names are stored in a single string with `|' as separator. */ ++static void ++blacklist_store_name (const char *name, ent_t *ent) ++{ ++ int namelen = strlen (name); ++ char *tmp; ++ ++ /* First call, setup cache. */ ++ if (ent->blacklist.size == 0) ++ { ++ ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); ++ ent->blacklist.data = malloc (ent->blacklist.size); ++ if (ent->blacklist.data == NULL) ++ return; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ ent->blacklist.current = 1; ++ } ++ else ++ { ++ if (in_blacklist (name, namelen, ent)) ++ return; /* no duplicates */ ++ ++ if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) ++ { ++ ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); ++ tmp = realloc (ent->blacklist.data, ent->blacklist.size); ++ if (tmp == NULL) ++ { ++ free (ent->blacklist.data); ++ ent->blacklist.size = 0; ++ return; ++ } ++ ent->blacklist.data = tmp; ++ } ++ } ++ ++ tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); ++ *tmp++ = '|'; ++ *tmp = '\0'; ++ ent->blacklist.current += namelen + 1; ++ ++ return; ++} ++ ++/* Return whether ent->blacklist contains name. */ ++static bool ++in_blacklist (const char *name, int namelen, ent_t *ent) ++{ ++ char buf[namelen + 3]; ++ char *cp; ++ ++ if (ent->blacklist.data == NULL) ++ return false; ++ ++ buf[0] = '|'; ++ cp = stpcpy (&buf[1], name); ++ *cp++ = '|'; ++ *cp = '\0'; ++ return strstr (ent->blacklist.data, buf) != NULL; ++} +Index: glibc-2.26/nss/nss_compat/compat-pwd.c +=================================================================== +--- /dev/null ++++ glibc-2.26/nss/nss_compat/compat-pwd.c +@@ -0,0 +1,1131 @@ ++/* Copyright (C) 1996-2017 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ Contributed by Thorsten Kukuk , 1996. ++ ++ 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 "netgroup.h" ++#include "nisdomain.h" ++ ++static service_user *ni; ++static enum nss_status (*nss_setpwent) (int stayopen); ++static enum nss_status (*nss_getpwnam_r) (const char *name, ++ struct passwd * pwd, char *buffer, ++ size_t buflen, int *errnop); ++static enum nss_status (*nss_getpwuid_r) (uid_t uid, struct passwd * pwd, ++ char *buffer, size_t buflen, ++ int *errnop); ++static enum nss_status (*nss_getpwent_r) (struct passwd * pwd, char *buffer, ++ size_t buflen, int *errnop); ++static enum nss_status (*nss_endpwent) (void); ++ ++/* Get the declaration of the parser function. */ ++#define ENTNAME pwent ++#define STRUCTURE passwd ++#define EXTERN_PARSER ++#include ++ ++/* Structure for remembering -@netgroup and -user members ... */ ++#define BLACKLIST_INITIAL_SIZE 512 ++#define BLACKLIST_INCREMENT 256 ++struct blacklist_t ++{ ++ char *data; ++ int current; ++ int size; ++}; ++ ++struct ent_t ++{ ++ bool netgroup; ++ bool first; ++ bool files; ++ enum nss_status setent_status; ++ FILE *stream; ++ struct blacklist_t blacklist; ++ struct passwd pwd; ++ struct __netgrent netgrdata; ++}; ++typedef struct ent_t ent_t; ++ ++static ent_t ext_ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, ++ { NULL, 0, 0 }, ++ { NULL, NULL, 0, 0, NULL, NULL, NULL }}; ++ ++/* Protect global state against multiple changers. */ ++__libc_lock_define_initialized (static, lock) ++ ++/* Prototypes for local functions. */ ++static void blacklist_store_name (const char *, ent_t *); ++static bool in_blacklist (const char *, int, ent_t *); ++ ++/* Initialize the NSS interface/functions. The calling function must ++ hold the lock. */ ++static void ++init_nss_interface (void) ++{ ++ if (__nss_database_lookup ("passwd_compat", NULL, "nis", &ni) >= 0) ++ { ++ nss_setpwent = __nss_lookup_function (ni, "setpwent"); ++ nss_getpwnam_r = __nss_lookup_function (ni, "getpwnam_r"); ++ nss_getpwuid_r = __nss_lookup_function (ni, "getpwuid_r"); ++ nss_getpwent_r = __nss_lookup_function (ni, "getpwent_r"); ++ nss_endpwent = __nss_lookup_function (ni, "endpwent"); ++ } ++} ++ ++static void ++give_pwd_free (struct passwd *pwd) ++{ ++ free (pwd->pw_name); ++ free (pwd->pw_passwd); ++ free (pwd->pw_gecos); ++ free (pwd->pw_dir); ++ free (pwd->pw_shell); ++ ++ memset (pwd, '\0', sizeof (struct passwd)); ++} ++ ++static size_t ++pwd_need_buflen (struct passwd *pwd) ++{ ++ size_t len = 0; ++ ++ if (pwd->pw_passwd != NULL) ++ len += strlen (pwd->pw_passwd) + 1; ++ ++ if (pwd->pw_gecos != NULL) ++ len += strlen (pwd->pw_gecos) + 1; ++ ++ if (pwd->pw_dir != NULL) ++ len += strlen (pwd->pw_dir) + 1; ++ ++ if (pwd->pw_shell != NULL) ++ len += strlen (pwd->pw_shell) + 1; ++ ++ return len; ++} ++ ++static void ++copy_pwd_changes (struct passwd *dest, struct passwd *src, ++ char *buffer, size_t buflen) ++{ ++ if (src->pw_passwd != NULL && strlen (src->pw_passwd)) ++ { ++ if (buffer == NULL) ++ dest->pw_passwd = strdup (src->pw_passwd); ++ else if (dest->pw_passwd && ++ strlen (dest->pw_passwd) >= strlen (src->pw_passwd)) ++ strcpy (dest->pw_passwd, src->pw_passwd); ++ else ++ { ++ dest->pw_passwd = buffer; ++ strcpy (dest->pw_passwd, src->pw_passwd); ++ buffer += strlen (dest->pw_passwd) + 1; ++ buflen = buflen - (strlen (dest->pw_passwd) + 1); ++ } ++ } ++ ++ if (src->pw_gecos != NULL && strlen (src->pw_gecos)) ++ { ++ if (buffer == NULL) ++ dest->pw_gecos = strdup (src->pw_gecos); ++ else if (dest->pw_gecos && ++ strlen (dest->pw_gecos) >= strlen (src->pw_gecos)) ++ strcpy (dest->pw_gecos, src->pw_gecos); ++ else ++ { ++ dest->pw_gecos = buffer; ++ strcpy (dest->pw_gecos, src->pw_gecos); ++ buffer += strlen (dest->pw_gecos) + 1; ++ buflen = buflen - (strlen (dest->pw_gecos) + 1); ++ } ++ } ++ if (src->pw_dir != NULL && strlen (src->pw_dir)) ++ { ++ if (buffer == NULL) ++ dest->pw_dir = strdup (src->pw_dir); ++ else if (dest->pw_dir && strlen (dest->pw_dir) >= strlen (src->pw_dir)) ++ strcpy (dest->pw_dir, src->pw_dir); ++ else ++ { ++ dest->pw_dir = buffer; ++ strcpy (dest->pw_dir, src->pw_dir); ++ buffer += strlen (dest->pw_dir) + 1; ++ buflen = buflen - (strlen (dest->pw_dir) + 1); ++ } ++ } ++ ++ if (src->pw_shell != NULL && strlen (src->pw_shell)) ++ { ++ if (buffer == NULL) ++ dest->pw_shell = strdup (src->pw_shell); ++ else if (dest->pw_shell && ++ strlen (dest->pw_shell) >= strlen (src->pw_shell)) ++ strcpy (dest->pw_shell, src->pw_shell); ++ else ++ { ++ dest->pw_shell = buffer; ++ strcpy (dest->pw_shell, src->pw_shell); ++ buffer += strlen (dest->pw_shell) + 1; ++ buflen = buflen - (strlen (dest->pw_shell) + 1); ++ } ++ } ++} ++ ++static enum nss_status ++internal_setpwent (ent_t *ent, int stayopen, int needent) ++{ ++ enum nss_status status = NSS_STATUS_SUCCESS; ++ ++ ent->first = ent->netgroup = false; ++ ent->files = true; ++ ent->setent_status = NSS_STATUS_SUCCESS; ++ ++ /* If something was left over free it. */ ++ if (ent->netgroup) ++ __internal_endnetgrent (&ent->netgrdata); ++ ++ if (ent->blacklist.data != NULL) ++ { ++ ent->blacklist.current = 1; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ } ++ else ++ ent->blacklist.current = 0; ++ ++ if (ent->stream == NULL) ++ { ++ ent->stream = fopen ("/etc/passwd", "rme"); ++ ++ if (ent->stream == NULL) ++ status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; ++ else ++ /* We take care of locking ourself. */ ++ __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); ++ } ++ else ++ rewind (ent->stream); ++ ++ give_pwd_free (&ent->pwd); ++ ++ if (needent && status == NSS_STATUS_SUCCESS && nss_setpwent) ++ ent->setent_status = nss_setpwent (stayopen); ++ ++ return status; ++} ++ ++ ++enum nss_status ++_nss_compat_setpwent (int stayopen) ++{ ++ enum nss_status result; ++ ++ __libc_lock_lock (lock); ++ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ result = internal_setpwent (&ext_ent, stayopen, 1); ++ ++ __libc_lock_unlock (lock); ++ ++ return result; ++} ++ ++ ++static enum nss_status ++internal_endpwent (ent_t *ent) ++{ ++ if (ent->stream != NULL) ++ { ++ fclose (ent->stream); ++ ent->stream = NULL; ++ } ++ ++ if (ent->netgroup) ++ __internal_endnetgrent (&ent->netgrdata); ++ ++ ent->first = ent->netgroup = false; ++ ++ if (ent->blacklist.data != NULL) ++ { ++ ent->blacklist.current = 1; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ } ++ else ++ ent->blacklist.current = 0; ++ ++ give_pwd_free (&ent->pwd); ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++enum nss_status ++_nss_compat_endpwent (void) ++{ ++ enum nss_status result; ++ ++ __libc_lock_lock (lock); ++ ++ if (nss_endpwent) ++ nss_endpwent (); ++ ++ result = internal_endpwent (&ext_ent); ++ ++ __libc_lock_unlock (lock); ++ ++ return result; ++} ++ ++ ++static enum nss_status ++getpwent_next_nss_netgr (const char *name, struct passwd *result, ent_t *ent, ++ char *group, char *buffer, size_t buflen, ++ int *errnop) ++{ ++ char *curdomain = NULL, *host, *user, *domain, *p2; ++ int status; ++ size_t p2len; ++ ++ /* Leave function if NSS module does not support getpwnam_r, ++ we need this function here. */ ++ if (!nss_getpwnam_r) ++ return NSS_STATUS_UNAVAIL; ++ ++ if (ent->first) ++ { ++ memset (&ent->netgrdata, 0, sizeof (struct __netgrent)); ++ __internal_setnetgrent (group, &ent->netgrdata); ++ ent->first = false; ++ } ++ ++ while (1) ++ { ++ status = __internal_getnetgrent_r (&host, &user, &domain, ++ &ent->netgrdata, buffer, buflen, ++ errnop); ++ if (status != 1) ++ { ++ __internal_endnetgrent (&ent->netgrdata); ++ ent->netgroup = 0; ++ give_pwd_free (&ent->pwd); ++ return NSS_STATUS_RETURN; ++ } ++ ++ if (user == NULL || user[0] == '-') ++ continue; ++ ++ if (domain != NULL) ++ { ++ if (curdomain == NULL ++ && __nss_get_default_domain (&curdomain) != 0) ++ { ++ __internal_endnetgrent (&ent->netgrdata); ++ ent->netgroup = false; ++ give_pwd_free (&ent->pwd); ++ return NSS_STATUS_UNAVAIL; ++ } ++ if (strcmp (curdomain, domain) != 0) ++ continue; ++ } ++ ++ /* If name != NULL, we are called from getpwnam. */ ++ if (name != NULL) ++ if (strcmp (user, name) != 0) ++ continue; ++ ++ p2len = pwd_need_buflen (&ent->pwd); ++ if (p2len > buflen) ++ { ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ p2 = buffer + (buflen - p2len); ++ buflen -= p2len; ++ ++ if (nss_getpwnam_r (user, result, buffer, buflen, errnop) != ++ NSS_STATUS_SUCCESS) ++ continue; ++ ++ if (!in_blacklist (result->pw_name, strlen (result->pw_name), ent)) ++ { ++ /* Store the User in the blacklist for possible the "+" at the ++ end of /etc/passwd */ ++ blacklist_store_name (result->pw_name, ent); ++ copy_pwd_changes (result, &ent->pwd, p2, p2len); ++ break; ++ } ++ } ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++/* get the next user from NSS (+ entry) */ ++static enum nss_status ++getpwent_next_nss (struct passwd *result, ent_t *ent, char *buffer, ++ size_t buflen, int *errnop) ++{ ++ enum nss_status status; ++ char *p2; ++ size_t p2len; ++ ++ /* Return if NSS module does not support getpwent_r. */ ++ if (!nss_getpwent_r) ++ return NSS_STATUS_UNAVAIL; ++ ++ /* If the setpwent call failed, say so. */ ++ if (ent->setent_status != NSS_STATUS_SUCCESS) ++ return ent->setent_status; ++ ++ p2len = pwd_need_buflen (&ent->pwd); ++ if (p2len > buflen) ++ { ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ p2 = buffer + (buflen - p2len); ++ buflen -= p2len; ++ ++ if (ent->first) ++ ent->first = false; ++ ++ do ++ { ++ if ((status = nss_getpwent_r (result, buffer, buflen, errnop)) != ++ NSS_STATUS_SUCCESS) ++ return status; ++ } ++ while (in_blacklist (result->pw_name, strlen (result->pw_name), ent)); ++ ++ copy_pwd_changes (result, &ent->pwd, p2, p2len); ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++/* This function handle the +user entrys in /etc/passwd */ ++static enum nss_status ++getpwnam_plususer (const char *name, struct passwd *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ if (!nss_getpwnam_r) ++ return NSS_STATUS_UNAVAIL; ++ ++ struct passwd pwd; ++ memset (&pwd, '\0', sizeof (struct passwd)); ++ ++ copy_pwd_changes (&pwd, result, NULL, 0); ++ ++ size_t plen = pwd_need_buflen (&pwd); ++ if (plen > buflen) ++ { ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ char *p = buffer + (buflen - plen); ++ buflen -= plen; ++ ++ enum nss_status status = nss_getpwnam_r (name, result, buffer, buflen, ++ errnop); ++ if (status != NSS_STATUS_SUCCESS) ++ return status; ++ ++ if (in_blacklist (result->pw_name, strlen (result->pw_name), ent)) ++ return NSS_STATUS_NOTFOUND; ++ ++ copy_pwd_changes (result, &pwd, p, plen); ++ give_pwd_free (&pwd); ++ /* We found the entry. */ ++ return NSS_STATUS_SUCCESS; ++} ++ ++static enum nss_status ++getpwent_next_file (struct passwd *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ struct parser_data *data = (void *) buffer; ++ while (1) ++ { ++ fpos_t pos; ++ char *p; ++ int parse_res; ++ ++ do ++ { ++ /* We need at least 3 characters for one line. */ ++ if (__glibc_unlikely (buflen < 3)) ++ { ++ erange: ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ ++ fgetpos (ent->stream, &pos); ++ buffer[buflen - 1] = '\xff'; ++ p = fgets_unlocked (buffer, buflen, ent->stream); ++ if (p == NULL && feof_unlocked (ent->stream)) ++ return NSS_STATUS_NOTFOUND; ++ ++ if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) ++ { ++ erange_reset: ++ fsetpos (ent->stream, &pos); ++ goto erange; ++ } ++ ++ /* Terminate the line for any case. */ ++ buffer[buflen - 1] = '\0'; ++ ++ /* Skip leading blanks. */ ++ while (isspace (*p)) ++ ++p; ++ } ++ while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ ++ /* Parse the line. If it is invalid, loop to ++ get the next line of the file to parse. */ ++ !(parse_res = _nss_files_parse_pwent (p, result, data, buflen, ++ errnop))); ++ ++ if (__glibc_unlikely (parse_res == -1)) ++ /* The parser ran out of space. */ ++ goto erange_reset; ++ ++ if (result->pw_name[0] != '+' && result->pw_name[0] != '-') ++ /* This is a real entry. */ ++ break; ++ ++ /* -@netgroup */ ++ if (result->pw_name[0] == '-' && result->pw_name[1] == '@' ++ && result->pw_name[2] != '\0') ++ { ++ /* XXX Do not use fixed length buffer. */ ++ char buf2[1024]; ++ char *user, *host, *domain; ++ struct __netgrent netgrdata; ++ ++ memset (&netgrdata, 0, sizeof (struct __netgrent)); ++ __internal_setnetgrent (&result->pw_name[2], &netgrdata); ++ while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata, ++ buf2, sizeof (buf2), errnop)) ++ { ++ if (user != NULL && user[0] != '-') ++ blacklist_store_name (user, ent); ++ } ++ __internal_endnetgrent (&netgrdata); ++ continue; ++ } ++ ++ /* +@netgroup */ ++ if (result->pw_name[0] == '+' && result->pw_name[1] == '@' ++ && result->pw_name[2] != '\0') ++ { ++ enum nss_status status; ++ ++ ent->netgroup = true; ++ ent->first = true; ++ copy_pwd_changes (&ent->pwd, result, NULL, 0); ++ ++ status = getpwent_next_nss_netgr (NULL, result, ent, ++ &result->pw_name[2], ++ buffer, buflen, errnop); ++ if (status == NSS_STATUS_RETURN) ++ continue; ++ else ++ return status; ++ } ++ ++ /* -user */ ++ if (result->pw_name[0] == '-' && result->pw_name[1] != '\0' ++ && result->pw_name[1] != '@') ++ { ++ blacklist_store_name (&result->pw_name[1], ent); ++ continue; ++ } ++ ++ /* +user */ ++ if (result->pw_name[0] == '+' && result->pw_name[1] != '\0' ++ && result->pw_name[1] != '@') ++ { ++ size_t len = strlen (result->pw_name); ++ char buf[len]; ++ enum nss_status status; ++ ++ /* Store the User in the blacklist for the "+" at the end of ++ /etc/passwd */ ++ memcpy (buf, &result->pw_name[1], len); ++ status = getpwnam_plususer (&result->pw_name[1], result, ent, ++ buffer, buflen, errnop); ++ blacklist_store_name (buf, ent); ++ ++ if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ ++ break; ++ else if (status == NSS_STATUS_RETURN /* We couldn't parse the entry */ ++ || status == NSS_STATUS_NOTFOUND) /* entry doesn't exist */ ++ continue; ++ else ++ { ++ if (status == NSS_STATUS_TRYAGAIN) ++ { ++ /* The parser ran out of space */ ++ fsetpos (ent->stream, &pos); ++ *errnop = ERANGE; ++ } ++ return status; ++ } ++ } ++ ++ /* +:... */ ++ if (result->pw_name[0] == '+' && result->pw_name[1] == '\0') ++ { ++ ent->files = false; ++ ent->first = true; ++ copy_pwd_changes (&ent->pwd, result, NULL, 0); ++ ++ return getpwent_next_nss (result, ent, buffer, buflen, errnop); ++ } ++ } ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++ ++static enum nss_status ++internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer, ++ size_t buflen, int *errnop) ++{ ++ if (ent->netgroup) ++ { ++ enum nss_status status; ++ ++ /* We are searching members in a netgroup */ ++ /* Since this is not the first call, we don't need the group name */ ++ status = getpwent_next_nss_netgr (NULL, pw, ent, NULL, buffer, buflen, ++ errnop); ++ if (status == NSS_STATUS_RETURN) ++ return getpwent_next_file (pw, ent, buffer, buflen, errnop); ++ else ++ return status; ++ } ++ else if (ent->files) ++ return getpwent_next_file (pw, ent, buffer, buflen, errnop); ++ else ++ return getpwent_next_nss (pw, ent, buffer, buflen, errnop); ++ ++} ++ ++enum nss_status ++_nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen, ++ int *errnop) ++{ ++ enum nss_status result = NSS_STATUS_SUCCESS; ++ ++ __libc_lock_lock (lock); ++ ++ /* Be prepared that the setpwent function was not called before. */ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ if (ext_ent.stream == NULL) ++ result = internal_setpwent (&ext_ent, 1, 1); ++ ++ if (result == NSS_STATUS_SUCCESS) ++ result = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop); ++ ++ __libc_lock_unlock (lock); ++ ++ return result; ++} ++ ++/* Searches in /etc/passwd and the NIS/NIS+ map for a special user */ ++static enum nss_status ++internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ struct parser_data *data = (void *) buffer; ++ ++ while (1) ++ { ++ fpos_t pos; ++ char *p; ++ int parse_res; ++ ++ do ++ { ++ /* We need at least 3 characters for one line. */ ++ if (__glibc_unlikely (buflen < 3)) ++ { ++ erange: ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ ++ fgetpos (ent->stream, &pos); ++ buffer[buflen - 1] = '\xff'; ++ p = fgets_unlocked (buffer, buflen, ent->stream); ++ if (p == NULL && feof_unlocked (ent->stream)) ++ { ++ return NSS_STATUS_NOTFOUND; ++ } ++ if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) ++ { ++ erange_reset: ++ fsetpos (ent->stream, &pos); ++ goto erange; ++ } ++ ++ /* Terminate the line for any case. */ ++ buffer[buflen - 1] = '\0'; ++ ++ /* Skip leading blanks. */ ++ while (isspace (*p)) ++ ++p; ++ } ++ while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ ++ /* Parse the line. If it is invalid, loop to ++ get the next line of the file to parse. */ ++ !(parse_res = _nss_files_parse_pwent (p, result, data, buflen, ++ errnop))); ++ ++ if (__glibc_unlikely (parse_res == -1)) ++ /* The parser ran out of space. */ ++ goto erange_reset; ++ ++ /* This is a real entry. */ ++ if (result->pw_name[0] != '+' && result->pw_name[0] != '-') ++ { ++ if (strcmp (result->pw_name, name) == 0) ++ return NSS_STATUS_SUCCESS; ++ else ++ continue; ++ } ++ ++ /* -@netgroup */ ++ if (result->pw_name[0] == '-' && result->pw_name[1] == '@' ++ && result->pw_name[2] != '\0') ++ { ++ if (innetgr (&result->pw_name[2], NULL, name, NULL)) ++ return NSS_STATUS_NOTFOUND; ++ continue; ++ } ++ ++ /* +@netgroup */ ++ if (result->pw_name[0] == '+' && result->pw_name[1] == '@' ++ && result->pw_name[2] != '\0') ++ { ++ enum nss_status status; ++ ++ if (innetgr (&result->pw_name[2], NULL, name, NULL)) ++ { ++ status = getpwnam_plususer (name, result, ent, buffer, ++ buflen, errnop); ++ ++ if (status == NSS_STATUS_RETURN) ++ continue; ++ ++ return status; ++ } ++ continue; ++ } ++ ++ /* -user */ ++ if (result->pw_name[0] == '-' && result->pw_name[1] != '\0' ++ && result->pw_name[1] != '@') ++ { ++ if (strcmp (&result->pw_name[1], name) == 0) ++ return NSS_STATUS_NOTFOUND; ++ else ++ continue; ++ } ++ ++ /* +user */ ++ if (result->pw_name[0] == '+' && result->pw_name[1] != '\0' ++ && result->pw_name[1] != '@') ++ { ++ if (strcmp (name, &result->pw_name[1]) == 0) ++ { ++ enum nss_status status; ++ ++ status = getpwnam_plususer (name, result, ent, buffer, buflen, ++ errnop); ++ if (status == NSS_STATUS_RETURN) ++ /* We couldn't parse the entry */ ++ return NSS_STATUS_NOTFOUND; ++ else ++ return status; ++ } ++ } ++ ++ /* +:... */ ++ if (result->pw_name[0] == '+' && result->pw_name[1] == '\0') ++ { ++ enum nss_status status; ++ ++ status = getpwnam_plususer (name, result, ent, ++ buffer, buflen, errnop); ++ if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ ++ break; ++ else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ ++ return NSS_STATUS_NOTFOUND; ++ else ++ return status; ++ } ++ } ++ return NSS_STATUS_SUCCESS; ++} ++ ++enum nss_status ++_nss_compat_getpwnam_r (const char *name, struct passwd *pwd, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ enum nss_status result; ++ ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }, ++ { NULL, NULL, 0, 0, NULL, NULL, NULL }}; ++ ++ if (name[0] == '-' || name[0] == '+') ++ return NSS_STATUS_NOTFOUND; ++ ++ __libc_lock_lock (lock); ++ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ __libc_lock_unlock (lock); ++ ++ result = internal_setpwent (&ent, 0, 0); ++ ++ if (result == NSS_STATUS_SUCCESS) ++ result = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop); ++ ++ internal_endpwent (&ent); ++ ++ return result; ++} ++ ++/* This function handle the + entry in /etc/passwd for getpwuid */ ++static enum nss_status ++getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer, ++ size_t buflen, int *errnop) ++{ ++ struct passwd pwd; ++ char *p; ++ size_t plen; ++ ++ if (!nss_getpwuid_r) ++ return NSS_STATUS_UNAVAIL; ++ ++ memset (&pwd, '\0', sizeof (struct passwd)); ++ ++ copy_pwd_changes (&pwd, result, NULL, 0); ++ ++ plen = pwd_need_buflen (&pwd); ++ if (plen > buflen) ++ { ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ p = buffer + (buflen - plen); ++ buflen -= plen; ++ ++ if (nss_getpwuid_r (uid, result, buffer, buflen, errnop) == ++ NSS_STATUS_SUCCESS) ++ { ++ copy_pwd_changes (result, &pwd, p, plen); ++ give_pwd_free (&pwd); ++ /* We found the entry. */ ++ return NSS_STATUS_SUCCESS; ++ } ++ else ++ { ++ /* Give buffer the old len back */ ++ buflen += plen; ++ give_pwd_free (&pwd); ++ } ++ return NSS_STATUS_RETURN; ++} ++ ++/* Searches in /etc/passwd and the NSS subsystem for a special user id */ ++static enum nss_status ++internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ struct parser_data *data = (void *) buffer; ++ ++ while (1) ++ { ++ fpos_t pos; ++ char *p; ++ int parse_res; ++ ++ do ++ { ++ /* We need at least 3 characters for one line. */ ++ if (__glibc_unlikely (buflen < 3)) ++ { ++ erange: ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ ++ fgetpos (ent->stream, &pos); ++ buffer[buflen - 1] = '\xff'; ++ p = fgets_unlocked (buffer, buflen, ent->stream); ++ if (p == NULL && feof_unlocked (ent->stream)) ++ return NSS_STATUS_NOTFOUND; ++ ++ if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) ++ { ++ erange_reset: ++ fsetpos (ent->stream, &pos); ++ goto erange; ++ } ++ ++ /* Terminate the line for any case. */ ++ buffer[buflen - 1] = '\0'; ++ ++ /* Skip leading blanks. */ ++ while (isspace (*p)) ++ ++p; ++ } ++ while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ ++ /* Parse the line. If it is invalid, loop to ++ get the next line of the file to parse. */ ++ !(parse_res = _nss_files_parse_pwent (p, result, data, buflen, ++ errnop))); ++ ++ if (__glibc_unlikely (parse_res == -1)) ++ /* The parser ran out of space. */ ++ goto erange_reset; ++ ++ /* This is a real entry. */ ++ if (result->pw_name[0] != '+' && result->pw_name[0] != '-') ++ { ++ if (result->pw_uid == uid) ++ return NSS_STATUS_SUCCESS; ++ else ++ continue; ++ } ++ ++ /* -@netgroup */ ++ if (result->pw_name[0] == '-' && result->pw_name[1] == '@' ++ && result->pw_name[2] != '\0') ++ { ++ /* -1, because we remove first two character of pw_name. */ ++ size_t len = strlen (result->pw_name) - 1; ++ char buf[len]; ++ enum nss_status status; ++ ++ memcpy (buf, &result->pw_name[2], len); ++ ++ status = getpwuid_plususer (uid, result, buffer, buflen, errnop); ++ if (status == NSS_STATUS_SUCCESS && ++ innetgr (buf, NULL, result->pw_name, NULL)) ++ return NSS_STATUS_NOTFOUND; ++ ++ continue; ++ } ++ ++ /* +@netgroup */ ++ if (result->pw_name[0] == '+' && result->pw_name[1] == '@' ++ && result->pw_name[2] != '\0') ++ { ++ /* -1, because we remove first two characters of pw_name. */ ++ size_t len = strlen (result->pw_name) - 1; ++ char buf[len]; ++ enum nss_status status; ++ ++ memcpy (buf, &result->pw_name[2], len); ++ ++ status = getpwuid_plususer (uid, result, buffer, buflen, errnop); ++ ++ if (status == NSS_STATUS_RETURN) ++ continue; ++ ++ if (status == NSS_STATUS_SUCCESS) ++ { ++ if (innetgr (buf, NULL, result->pw_name, NULL)) ++ return NSS_STATUS_SUCCESS; ++ } ++ else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ ++ return NSS_STATUS_NOTFOUND; ++ else ++ return status; ++ ++ continue; ++ } ++ ++ /* -user */ ++ if (result->pw_name[0] == '-' && result->pw_name[1] != '\0' ++ && result->pw_name[1] != '@') ++ { ++ size_t len = strlen (result->pw_name); ++ char buf[len]; ++ enum nss_status status; ++ ++ memcpy (buf, &result->pw_name[1], len); ++ ++ status = getpwuid_plususer (uid, result, buffer, buflen, errnop); ++ if (status == NSS_STATUS_SUCCESS && ++ innetgr (buf, NULL, result->pw_name, NULL)) ++ return NSS_STATUS_NOTFOUND; ++ continue; ++ } ++ ++ /* +user */ ++ if (result->pw_name[0] == '+' && result->pw_name[1] != '\0' ++ && result->pw_name[1] != '@') ++ { ++ size_t len = strlen (result->pw_name); ++ char buf[len]; ++ enum nss_status status; ++ ++ memcpy (buf, &result->pw_name[1], len); ++ ++ status = getpwuid_plususer (uid, result, buffer, buflen, errnop); ++ ++ if (status == NSS_STATUS_RETURN) ++ continue; ++ ++ if (status == NSS_STATUS_SUCCESS) ++ { ++ if (strcmp (buf, result->pw_name) == 0) ++ return NSS_STATUS_SUCCESS; ++ } ++ else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ ++ return NSS_STATUS_NOTFOUND; ++ else ++ return status; ++ ++ continue; ++ } ++ ++ /* +:... */ ++ if (result->pw_name[0] == '+' && result->pw_name[1] == '\0') ++ { ++ enum nss_status status; ++ ++ status = getpwuid_plususer (uid, result, buffer, buflen, errnop); ++ if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ ++ break; ++ else if (status == NSS_STATUS_RETURN) /* We couldn't parse the entry */ ++ return NSS_STATUS_NOTFOUND; ++ else ++ return status; ++ } ++ } ++ return NSS_STATUS_SUCCESS; ++} ++ ++enum nss_status ++_nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ enum nss_status result; ++ ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 }, ++ { NULL, NULL, 0, 0, NULL, NULL, NULL }}; ++ ++ __libc_lock_lock (lock); ++ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ __libc_lock_unlock (lock); ++ ++ result = internal_setpwent (&ent, 0, 0); ++ ++ if (result == NSS_STATUS_SUCCESS) ++ result = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop); ++ ++ internal_endpwent (&ent); ++ ++ return result; ++} ++ ++ ++/* Support routines for remembering -@netgroup and -user entries. ++ The names are stored in a single string with `|' as separator. */ ++static void ++blacklist_store_name (const char *name, ent_t *ent) ++{ ++ int namelen = strlen (name); ++ char *tmp; ++ ++ /* first call, setup cache */ ++ if (ent->blacklist.size == 0) ++ { ++ ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); ++ ent->blacklist.data = malloc (ent->blacklist.size); ++ if (ent->blacklist.data == NULL) ++ return; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ ent->blacklist.current = 1; ++ } ++ else ++ { ++ if (in_blacklist (name, namelen, ent)) ++ return; /* no duplicates */ ++ ++ if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) ++ { ++ ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); ++ tmp = realloc (ent->blacklist.data, ent->blacklist.size); ++ if (tmp == NULL) ++ { ++ free (ent->blacklist.data); ++ ent->blacklist.size = 0; ++ return; ++ } ++ ent->blacklist.data = tmp; ++ } ++ } ++ ++ tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); ++ *tmp++ = '|'; ++ *tmp = '\0'; ++ ent->blacklist.current += namelen + 1; ++ ++ return; ++} ++ ++/* Returns whether ent->blacklist contains name. */ ++static bool ++in_blacklist (const char *name, int namelen, ent_t *ent) ++{ ++ char buf[namelen + 3]; ++ char *cp; ++ ++ if (ent->blacklist.data == NULL) ++ return false; ++ ++ buf[0] = '|'; ++ cp = stpcpy (&buf[1], name); ++ *cp++ = '|'; ++ *cp = '\0'; ++ return strstr (ent->blacklist.data, buf) != NULL; ++} +Index: glibc-2.26/nss/nss_compat/compat-spwd.c +=================================================================== +--- /dev/null ++++ glibc-2.26/nss/nss_compat/compat-spwd.c +@@ -0,0 +1,857 @@ ++/* Copyright (C) 1996-2017 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ Contributed by Thorsten Kukuk , 1996. ++ ++ 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 "netgroup.h" ++#include "nisdomain.h" ++ ++static service_user *ni; ++static enum nss_status (*nss_setspent) (int stayopen); ++static enum nss_status (*nss_getspnam_r) (const char *name, struct spwd * sp, ++ char *buffer, size_t buflen, ++ int *errnop); ++static enum nss_status (*nss_getspent_r) (struct spwd * sp, char *buffer, ++ size_t buflen, int *errnop); ++static enum nss_status (*nss_endspent) (void); ++ ++/* Get the declaration of the parser function. */ ++#define ENTNAME spent ++#define STRUCTURE spwd ++#define EXTERN_PARSER ++#include ++ ++/* Structure for remembering -@netgroup and -user members ... */ ++#define BLACKLIST_INITIAL_SIZE 512 ++#define BLACKLIST_INCREMENT 256 ++struct blacklist_t ++{ ++ char *data; ++ int current; ++ int size; ++}; ++ ++struct ent_t ++{ ++ bool netgroup; ++ bool files; ++ bool first; ++ enum nss_status setent_status; ++ FILE *stream; ++ struct blacklist_t blacklist; ++ struct spwd pwd; ++ struct __netgrent netgrdata; ++}; ++typedef struct ent_t ent_t; ++ ++static ent_t ext_ent = { false, true, false, NSS_STATUS_SUCCESS, NULL, ++ { NULL, 0, 0}, ++ { NULL, NULL, 0, 0, 0, 0, 0, 0, 0}}; ++ ++/* Protect global state against multiple changers. */ ++__libc_lock_define_initialized (static, lock) ++ ++/* Prototypes for local functions. */ ++static void blacklist_store_name (const char *, ent_t *); ++static bool in_blacklist (const char *, int, ent_t *); ++ ++/* Initialize the NSS interface/functions. The calling function must ++ hold the lock. */ ++static void ++init_nss_interface (void) ++{ ++ if (__nss_database_lookup ("shadow_compat", "passwd_compat", ++ "nis", &ni) >= 0) ++ { ++ nss_setspent = __nss_lookup_function (ni, "setspent"); ++ nss_getspnam_r = __nss_lookup_function (ni, "getspnam_r"); ++ nss_getspent_r = __nss_lookup_function (ni, "getspent_r"); ++ nss_endspent = __nss_lookup_function (ni, "endspent"); ++ } ++} ++ ++static void ++give_spwd_free (struct spwd *pwd) ++{ ++ free (pwd->sp_namp); ++ free (pwd->sp_pwdp); ++ ++ memset (pwd, '\0', sizeof (struct spwd)); ++ pwd->sp_warn = -1; ++ pwd->sp_inact = -1; ++ pwd->sp_expire = -1; ++ pwd->sp_flag = ~0ul; ++} ++ ++static int ++spwd_need_buflen (struct spwd *pwd) ++{ ++ int len = 0; ++ ++ if (pwd->sp_pwdp != NULL) ++ len += strlen (pwd->sp_pwdp) + 1; ++ ++ return len; ++} ++ ++static void ++copy_spwd_changes (struct spwd *dest, struct spwd *src, ++ char *buffer, size_t buflen) ++{ ++ if (src->sp_pwdp != NULL && strlen (src->sp_pwdp)) ++ { ++ if (buffer == NULL) ++ dest->sp_pwdp = strdup (src->sp_pwdp); ++ else if (dest->sp_pwdp && ++ strlen (dest->sp_pwdp) >= strlen (src->sp_pwdp)) ++ strcpy (dest->sp_pwdp, src->sp_pwdp); ++ else ++ { ++ dest->sp_pwdp = buffer; ++ strcpy (dest->sp_pwdp, src->sp_pwdp); ++ buffer += strlen (dest->sp_pwdp) + 1; ++ buflen = buflen - (strlen (dest->sp_pwdp) + 1); ++ } ++ } ++ if (src->sp_lstchg != 0) ++ dest->sp_lstchg = src->sp_lstchg; ++ if (src->sp_min != 0) ++ dest->sp_min = src->sp_min; ++ if (src->sp_max != 0) ++ dest->sp_max = src->sp_max; ++ if (src->sp_warn != -1) ++ dest->sp_warn = src->sp_warn; ++ if (src->sp_inact != -1) ++ dest->sp_inact = src->sp_inact; ++ if (src->sp_expire != -1) ++ dest->sp_expire = src->sp_expire; ++ if (src->sp_flag != ~0ul) ++ dest->sp_flag = src->sp_flag; ++} ++ ++static enum nss_status ++internal_setspent (ent_t *ent, int stayopen, int needent) ++{ ++ enum nss_status status = NSS_STATUS_SUCCESS; ++ ++ ent->first = ent->netgroup = 0; ++ ent->files = true; ++ ++ /* If something was left over free it. */ ++ if (ent->netgroup) ++ __internal_endnetgrent (&ent->netgrdata); ++ ++ if (ent->blacklist.data != NULL) ++ { ++ ent->blacklist.current = 1; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ } ++ else ++ ent->blacklist.current = 0; ++ ++ if (ent->stream == NULL) ++ { ++ ent->stream = fopen ("/etc/shadow", "rme"); ++ ++ if (ent->stream == NULL) ++ status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL; ++ else ++ /* We take care of locking ourself. */ ++ __fsetlocking (ent->stream, FSETLOCKING_BYCALLER); ++ } ++ else ++ rewind (ent->stream); ++ ++ give_spwd_free (&ent->pwd); ++ ++ if (needent && status == NSS_STATUS_SUCCESS && nss_setspent) ++ ent->setent_status = nss_setspent (stayopen); ++ ++ return status; ++} ++ ++ ++enum nss_status ++_nss_compat_setspent (int stayopen) ++{ ++ enum nss_status result; ++ ++ __libc_lock_lock (lock); ++ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ result = internal_setspent (&ext_ent, stayopen, 1); ++ ++ __libc_lock_unlock (lock); ++ ++ return result; ++} ++ ++ ++static enum nss_status ++internal_endspent (ent_t *ent) ++{ ++ if (ent->stream != NULL) ++ { ++ fclose (ent->stream); ++ ent->stream = NULL; ++ } ++ ++ if (ent->netgroup) ++ __internal_endnetgrent (&ent->netgrdata); ++ ++ ent->first = ent->netgroup = false; ++ ent->files = true; ++ ++ if (ent->blacklist.data != NULL) ++ { ++ ent->blacklist.current = 1; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ } ++ else ++ ent->blacklist.current = 0; ++ ++ give_spwd_free (&ent->pwd); ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++enum nss_status ++_nss_compat_endspent (void) ++{ ++ enum nss_status result; ++ ++ __libc_lock_lock (lock); ++ ++ if (nss_endspent) ++ nss_endspent (); ++ ++ result = internal_endspent (&ext_ent); ++ ++ __libc_lock_unlock (lock); ++ ++ return result; ++} ++ ++ ++static enum nss_status ++getspent_next_nss_netgr (const char *name, struct spwd *result, ent_t *ent, ++ char *group, char *buffer, size_t buflen, ++ int *errnop) ++{ ++ char *curdomain = NULL, *host, *user, *domain, *p2; ++ size_t p2len; ++ ++ if (!nss_getspnam_r) ++ return NSS_STATUS_UNAVAIL; ++ ++ /* If the setpwent call failed, say so. */ ++ if (ent->setent_status != NSS_STATUS_SUCCESS) ++ return ent->setent_status; ++ ++ if (ent->first) ++ { ++ memset (&ent->netgrdata, 0, sizeof (struct __netgrent)); ++ __internal_setnetgrent (group, &ent->netgrdata); ++ ent->first = false; ++ } ++ ++ while (1) ++ { ++ enum nss_status status; ++ ++ status = __internal_getnetgrent_r (&host, &user, &domain, ++ &ent->netgrdata, buffer, buflen, ++ errnop); ++ if (status != 1) ++ { ++ __internal_endnetgrent (&ent->netgrdata); ++ ent->netgroup = false; ++ give_spwd_free (&ent->pwd); ++ return NSS_STATUS_RETURN; ++ } ++ ++ if (user == NULL || user[0] == '-') ++ continue; ++ ++ if (domain != NULL) ++ { ++ if (curdomain == NULL ++ && __nss_get_default_domain (&curdomain) != 0) ++ { ++ __internal_endnetgrent (&ent->netgrdata); ++ ent->netgroup = false; ++ give_spwd_free (&ent->pwd); ++ return NSS_STATUS_UNAVAIL; ++ } ++ if (strcmp (curdomain, domain) != 0) ++ continue; ++ } ++ ++ /* If name != NULL, we are called from getpwnam */ ++ if (name != NULL) ++ if (strcmp (user, name) != 0) ++ continue; ++ ++ p2len = spwd_need_buflen (&ent->pwd); ++ if (p2len > buflen) ++ { ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ p2 = buffer + (buflen - p2len); ++ buflen -= p2len; ++ ++ if (nss_getspnam_r (user, result, buffer, buflen, errnop) != ++ NSS_STATUS_SUCCESS) ++ continue; ++ ++ if (!in_blacklist (result->sp_namp, strlen (result->sp_namp), ent)) ++ { ++ /* Store the User in the blacklist for possible the "+" at the ++ end of /etc/passwd */ ++ blacklist_store_name (result->sp_namp, ent); ++ copy_spwd_changes (result, &ent->pwd, p2, p2len); ++ break; ++ } ++ } ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++ ++static enum nss_status ++getspent_next_nss (struct spwd *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ enum nss_status status; ++ char *p2; ++ size_t p2len; ++ ++ if (!nss_getspent_r) ++ return NSS_STATUS_UNAVAIL; ++ ++ p2len = spwd_need_buflen (&ent->pwd); ++ if (p2len > buflen) ++ { ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ p2 = buffer + (buflen - p2len); ++ buflen -= p2len; ++ do ++ { ++ if ((status = nss_getspent_r (result, buffer, buflen, errnop)) != ++ NSS_STATUS_SUCCESS) ++ return status; ++ } ++ while (in_blacklist (result->sp_namp, strlen (result->sp_namp), ent)); ++ ++ copy_spwd_changes (result, &ent->pwd, p2, p2len); ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++ ++/* This function handle the +user entrys in /etc/shadow */ ++static enum nss_status ++getspnam_plususer (const char *name, struct spwd *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ if (!nss_getspnam_r) ++ return NSS_STATUS_UNAVAIL; ++ ++ struct spwd pwd; ++ memset (&pwd, '\0', sizeof (struct spwd)); ++ pwd.sp_warn = -1; ++ pwd.sp_inact = -1; ++ pwd.sp_expire = -1; ++ pwd.sp_flag = ~0ul; ++ ++ copy_spwd_changes (&pwd, result, NULL, 0); ++ ++ size_t plen = spwd_need_buflen (&pwd); ++ if (plen > buflen) ++ { ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ char *p = buffer + (buflen - plen); ++ buflen -= plen; ++ ++ enum nss_status status = nss_getspnam_r (name, result, buffer, buflen, ++ errnop); ++ if (status != NSS_STATUS_SUCCESS) ++ return status; ++ ++ if (in_blacklist (result->sp_namp, strlen (result->sp_namp), ent)) ++ return NSS_STATUS_NOTFOUND; ++ ++ copy_spwd_changes (result, &pwd, p, plen); ++ give_spwd_free (&pwd); ++ /* We found the entry. */ ++ return NSS_STATUS_SUCCESS; ++} ++ ++ ++static enum nss_status ++getspent_next_file (struct spwd *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ struct parser_data *data = (void *) buffer; ++ while (1) ++ { ++ fpos_t pos; ++ int parse_res = 0; ++ char *p; ++ ++ do ++ { ++ /* We need at least 3 characters for one line. */ ++ if (__glibc_unlikely (buflen < 3)) ++ { ++ erange: ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ ++ fgetpos (ent->stream, &pos); ++ buffer[buflen - 1] = '\xff'; ++ p = fgets_unlocked (buffer, buflen, ent->stream); ++ if (p == NULL && feof_unlocked (ent->stream)) ++ return NSS_STATUS_NOTFOUND; ++ ++ if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0)) ++ { ++ erange_reset: ++ fsetpos (ent->stream, &pos); ++ goto erange; ++ } ++ ++ /* Skip leading blanks. */ ++ while (isspace (*p)) ++ ++p; ++ } ++ while (*p == '\0' || *p == '#' /* Ignore empty and comment lines. */ ++ /* Parse the line. If it is invalid, loop to ++ get the next line of the file to parse. */ ++ || !(parse_res = _nss_files_parse_spent (p, result, data, ++ buflen, errnop))); ++ ++ if (__glibc_unlikely (parse_res == -1)) ++ /* The parser ran out of space. */ ++ goto erange_reset; ++ ++ if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-') ++ /* This is a real entry. */ ++ break; ++ ++ /* -@netgroup */ ++ if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@' ++ && result->sp_namp[2] != '\0') ++ { ++ /* XXX Do not use fixed length buffers. */ ++ char buf2[1024]; ++ char *user, *host, *domain; ++ struct __netgrent netgrdata; ++ ++ memset (&netgrdata, 0, sizeof (struct __netgrent)); ++ __internal_setnetgrent (&result->sp_namp[2], &netgrdata); ++ while (__internal_getnetgrent_r (&host, &user, &domain, ++ &netgrdata, buf2, sizeof (buf2), ++ errnop)) ++ { ++ if (user != NULL && user[0] != '-') ++ blacklist_store_name (user, ent); ++ } ++ __internal_endnetgrent (&netgrdata); ++ continue; ++ } ++ ++ /* +@netgroup */ ++ if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@' ++ && result->sp_namp[2] != '\0') ++ { ++ int status; ++ ++ ent->netgroup = true; ++ ent->first = true; ++ copy_spwd_changes (&ent->pwd, result, NULL, 0); ++ ++ status = getspent_next_nss_netgr (NULL, result, ent, ++ &result->sp_namp[2], ++ buffer, buflen, errnop); ++ if (status == NSS_STATUS_RETURN) ++ continue; ++ else ++ return status; ++ } ++ ++ /* -user */ ++ if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0' ++ && result->sp_namp[1] != '@') ++ { ++ blacklist_store_name (&result->sp_namp[1], ent); ++ continue; ++ } ++ ++ /* +user */ ++ if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0' ++ && result->sp_namp[1] != '@') ++ { ++ size_t len = strlen (result->sp_namp); ++ char buf[len]; ++ enum nss_status status; ++ ++ /* Store the User in the blacklist for the "+" at the end of ++ /etc/passwd */ ++ memcpy (buf, &result->sp_namp[1], len); ++ status = getspnam_plususer (&result->sp_namp[1], result, ent, ++ buffer, buflen, errnop); ++ blacklist_store_name (buf, ent); ++ ++ if (status == NSS_STATUS_SUCCESS) /* We found the entry. */ ++ break; ++ /* We couldn't parse the entry */ ++ else if (status == NSS_STATUS_RETURN ++ /* entry doesn't exist */ ++ || status == NSS_STATUS_NOTFOUND) ++ continue; ++ else ++ { ++ if (status == NSS_STATUS_TRYAGAIN) ++ { ++ fsetpos (ent->stream, &pos); ++ *errnop = ERANGE; ++ } ++ return status; ++ } ++ } ++ ++ /* +:... */ ++ if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0') ++ { ++ ent->files = false; ++ ent->first = true; ++ copy_spwd_changes (&ent->pwd, result, NULL, 0); ++ ++ return getspent_next_nss (result, ent, buffer, buflen, errnop); ++ } ++ } ++ ++ return NSS_STATUS_SUCCESS; ++} ++ ++ ++static enum nss_status ++internal_getspent_r (struct spwd *pw, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ if (ent->netgroup) ++ { ++ enum nss_status status; ++ ++ /* We are searching members in a netgroup */ ++ /* Since this is not the first call, we don't need the group name */ ++ status = getspent_next_nss_netgr (NULL, pw, ent, NULL, buffer, ++ buflen, errnop); ++ ++ if (status == NSS_STATUS_RETURN) ++ return getspent_next_file (pw, ent, buffer, buflen, errnop); ++ else ++ return status; ++ } ++ else if (ent->files) ++ return getspent_next_file (pw, ent, buffer, buflen, errnop); ++ else ++ return getspent_next_nss (pw, ent, buffer, buflen, errnop); ++} ++ ++ ++enum nss_status ++_nss_compat_getspent_r (struct spwd *pwd, char *buffer, size_t buflen, ++ int *errnop) ++{ ++ enum nss_status result = NSS_STATUS_SUCCESS; ++ ++ __libc_lock_lock (lock); ++ ++ /* Be prepared that the setpwent function was not called before. */ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ if (ext_ent.stream == NULL) ++ result = internal_setspent (&ext_ent, 1, 1); ++ ++ if (result == NSS_STATUS_SUCCESS) ++ result = internal_getspent_r (pwd, &ext_ent, buffer, buflen, errnop); ++ ++ __libc_lock_unlock (lock); ++ ++ return result; ++} ++ ++ ++/* Searches in /etc/passwd and the NIS/NIS+ map for a special user */ ++static enum nss_status ++internal_getspnam_r (const char *name, struct spwd *result, ent_t *ent, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ struct parser_data *data = (void *) buffer; ++ ++ while (1) ++ { ++ fpos_t pos; ++ char *p; ++ int parse_res; ++ ++ do ++ { ++ /* We need at least 3 characters for one line. */ ++ if (__glibc_unlikely (buflen < 3)) ++ { ++ erange: ++ *errnop = ERANGE; ++ return NSS_STATUS_TRYAGAIN; ++ } ++ ++ fgetpos (ent->stream, &pos); ++ buffer[buflen - 1] = '\xff'; ++ p = fgets_unlocked (buffer, buflen, ent->stream); ++ if (p == NULL && feof_unlocked (ent->stream)) ++ return NSS_STATUS_NOTFOUND; ++ ++ if (p == NULL || buffer[buflen - 1] != '\xff') ++ { ++ erange_reset: ++ fsetpos (ent->stream, &pos); ++ goto erange; ++ } ++ ++ /* Terminate the line for any case. */ ++ buffer[buflen - 1] = '\0'; ++ ++ /* Skip leading blanks. */ ++ while (isspace (*p)) ++ ++p; ++ } ++ while (*p == '\0' || *p == '#' || /* Ignore empty and comment lines. */ ++ /* Parse the line. If it is invalid, loop to ++ get the next line of the file to parse. */ ++ !(parse_res = _nss_files_parse_spent (p, result, data, buflen, ++ errnop))); ++ ++ if (__glibc_unlikely (parse_res == -1)) ++ /* The parser ran out of space. */ ++ goto erange_reset; ++ ++ /* This is a real entry. */ ++ if (result->sp_namp[0] != '+' && result->sp_namp[0] != '-') ++ { ++ if (strcmp (result->sp_namp, name) == 0) ++ return NSS_STATUS_SUCCESS; ++ else ++ continue; ++ } ++ ++ /* -@netgroup */ ++ /* If the loaded NSS module does not support this service, add ++ all users from a +@netgroup entry to the blacklist, too. */ ++ if (result->sp_namp[0] == '-' && result->sp_namp[1] == '@' ++ && result->sp_namp[2] != '\0') ++ { ++ if (innetgr (&result->sp_namp[2], NULL, name, NULL)) ++ return NSS_STATUS_NOTFOUND; ++ continue; ++ } ++ ++ /* +@netgroup */ ++ if (result->sp_namp[0] == '+' && result->sp_namp[1] == '@' ++ && result->sp_namp[2] != '\0') ++ { ++ enum nss_status status; ++ ++ if (innetgr (&result->sp_namp[2], NULL, name, NULL)) ++ { ++ status = getspnam_plususer (name, result, ent, buffer, ++ buflen, errnop); ++ ++ if (status == NSS_STATUS_RETURN) ++ continue; ++ ++ return status; ++ } ++ continue; ++ } ++ ++ /* -user */ ++ if (result->sp_namp[0] == '-' && result->sp_namp[1] != '\0' ++ && result->sp_namp[1] != '@') ++ { ++ if (strcmp (&result->sp_namp[1], name) == 0) ++ return NSS_STATUS_NOTFOUND; ++ else ++ continue; ++ } ++ ++ /* +user */ ++ if (result->sp_namp[0] == '+' && result->sp_namp[1] != '\0' ++ && result->sp_namp[1] != '@') ++ { ++ if (strcmp (name, &result->sp_namp[1]) == 0) ++ { ++ enum nss_status status; ++ ++ status = getspnam_plususer (name, result, ent, ++ buffer, buflen, errnop); ++ ++ if (status == NSS_STATUS_RETURN) ++ /* We couldn't parse the entry */ ++ return NSS_STATUS_NOTFOUND; ++ else ++ return status; ++ } ++ } ++ ++ /* +:... */ ++ if (result->sp_namp[0] == '+' && result->sp_namp[1] == '\0') ++ { ++ enum nss_status status; ++ ++ status = getspnam_plususer (name, result, ent, ++ buffer, buflen, errnop); ++ ++ if (status == NSS_STATUS_SUCCESS) ++ /* We found the entry. */ ++ break; ++ else if (status == NSS_STATUS_RETURN) ++ /* We couldn't parse the entry */ ++ return NSS_STATUS_NOTFOUND; ++ else ++ return status; ++ } ++ } ++ return NSS_STATUS_SUCCESS; ++} ++ ++ ++enum nss_status ++_nss_compat_getspnam_r (const char *name, struct spwd *pwd, ++ char *buffer, size_t buflen, int *errnop) ++{ ++ enum nss_status result; ++ ent_t ent = { false, true, false, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0}, ++ { NULL, NULL, 0, 0, 0, 0, 0, 0, 0}}; ++ ++ if (name[0] == '-' || name[0] == '+') ++ return NSS_STATUS_NOTFOUND; ++ ++ __libc_lock_lock (lock); ++ ++ if (ni == NULL) ++ init_nss_interface (); ++ ++ __libc_lock_unlock (lock); ++ ++ result = internal_setspent (&ent, 0, 0); ++ ++ if (result == NSS_STATUS_SUCCESS) ++ result = internal_getspnam_r (name, pwd, &ent, buffer, buflen, errnop); ++ ++ internal_endspent (&ent); ++ ++ return result; ++} ++ ++ ++/* Support routines for remembering -@netgroup and -user entries. ++ The names are stored in a single string with `|' as separator. */ ++static void ++blacklist_store_name (const char *name, ent_t *ent) ++{ ++ int namelen = strlen (name); ++ char *tmp; ++ ++ /* first call, setup cache */ ++ if (ent->blacklist.size == 0) ++ { ++ ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen); ++ ent->blacklist.data = malloc (ent->blacklist.size); ++ if (ent->blacklist.data == NULL) ++ return; ++ ent->blacklist.data[0] = '|'; ++ ent->blacklist.data[1] = '\0'; ++ ent->blacklist.current = 1; ++ } ++ else ++ { ++ if (in_blacklist (name, namelen, ent)) ++ return; /* no duplicates */ ++ ++ if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size) ++ { ++ ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen); ++ tmp = realloc (ent->blacklist.data, ent->blacklist.size); ++ if (tmp == NULL) ++ { ++ free (ent->blacklist.data); ++ ent->blacklist.size = 0; ++ return; ++ } ++ ent->blacklist.data = tmp; ++ } ++ } ++ ++ tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name); ++ *tmp++ = '|'; ++ *tmp = '\0'; ++ ent->blacklist.current += namelen + 1; ++ ++ return; ++} ++ ++ ++/* Returns whether ent->blacklist contains name. */ ++static bool ++in_blacklist (const char *name, int namelen, ent_t *ent) ++{ ++ char buf[namelen + 3]; ++ char *cp; ++ ++ if (ent->blacklist.data == NULL) ++ return false; ++ ++ buf[0] = '|'; ++ cp = stpcpy (&buf[1], name); ++ *cp++ = '|'; ++ *cp = '\0'; ++ return strstr (ent->blacklist.data, buf) != NULL; ++} +Index: glibc-2.26/nss/nss_compat/nisdomain.c +=================================================================== +--- /dev/null ++++ glibc-2.26/nss/nss_compat/nisdomain.c +@@ -0,0 +1,58 @@ ++/* Copyright (C) 2017 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 "nisdomain.h" ++ ++#define MAXDOMAINNAMELEN 1024 ++ ++static char domainname[MAXDOMAINNAMELEN]; ++ ++__libc_lock_define_initialized (static, domainname_lock) ++ ++int ++__nss_get_default_domain (char **outdomain) ++{ ++ int result = 0; ++ *outdomain = NULL; ++ ++ __libc_lock_lock (domainname_lock); ++ ++ if (domainname[0] != '\0') ++ { ++ if (getdomainname (domainname, MAXDOMAINNAMELEN) < 0) ++ result = errno; ++ else if (strcmp (domainname, "(none)") == 0) ++ { ++ /* If domainname is not set, some systems will return "(none)" */ ++ domainname[0] = '\0'; ++ result = ENOENT; ++ } ++ else ++ *outdomain = domainname; ++ } ++ else ++ *outdomain = domainname; ++ ++ __libc_lock_unlock (domainname_lock); ++ ++ return result; ++} +Index: glibc-2.26/nss/nss_compat/nisdomain.h +=================================================================== +--- /dev/null ++++ glibc-2.26/nss/nss_compat/nisdomain.h +@@ -0,0 +1,20 @@ ++/* Copyright (C) 2017 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 OUTDOMAIN to a pointer to the current NIS domain name, or NULL if ++ not set. Return zero on success, an error number on failure. */ ++extern int __nss_get_default_domain (char **outdomain); diff --git a/nsswitch.conf b/nsswitch.conf index fcf5e1a..e0c7d96 100644 --- a/nsswitch.conf +++ b/nsswitch.conf @@ -22,9 +22,9 @@ # For more information, please read the nsswitch.conf.5 manual page. # -passwd: files -group: files -shadow: files +passwd: compat +group: compat +shadow: compat hosts: files dns networks: files dns