From a999deb25a0ffad18defbadd6c6fb3bfc70fbe3d Mon Sep 17 00:00:00 2001 From: Carlos O'Donell Date: Jan 13 2016 17:59:35 +0000 Subject: Resolves: #1146822 - Add group merging support for distributed management (#1146822). --- diff --git a/glibc-nsswitch-Add-group-merging-support.patch b/glibc-nsswitch-Add-group-merging-support.patch new file mode 100644 index 0000000..0188652 --- /dev/null +++ b/glibc-nsswitch-Add-group-merging-support.patch @@ -0,0 +1,858 @@ +Content-Type: text/plain; charset="utf-8" +MIME-Version: 1.0 +Content-Transfer-Encoding: 7bit +Subject: nsswitch: Add group merging support +From: Stephen Gallagher +X-Patchwork-Id: 10289 +Message-Id: <1452213991-6499-1-git-send-email-sgallagh@redhat.com> +To: libc-alpha@sourceware.org +Date: Thu, 7 Jan 2016 19:46:31 -0500 + +https://sourceware.org/glibc/wiki/Proposals/GroupMerging + +== Justification == +It is common today for users to rely on centrally-managed user stores for +handling their user accounts. However, much software existing today does +not have an innate understanding of such accounts. Instead, they commonly +rely on membership in known groups for managing access-control (for +example the "wheel" group on Fedora and RHEL systems or the "adm" group +on Debian-derived systems). In the present incarnation of nsswitch, the +only way to have such groups managed by a remote user store such as +FreeIPA or Active Directory would be to manually remove the groups from +/etc/group on the clients so that nsswitch would then move past nss_files +and into the SSSD, nss-ldap or other remote user database. + +== Solution == +With this patch, a new action is introduced for nsswitch: +NSS_ACTION_MERGE. To take advantage of it, one will add [SUCCESS=merge] +between two database entries in the nsswitch.conf file. When a group is +located in the first of the two group entries, processing will continue +on to the next one. If the group is also found in the next entry (and the +group name and GID are an exact match), the member list of the second +entry will be added to the group object to be returned. + +== Implementation == +After each DL_LOOKUP_FN() returns, the next action is checked. If the +function returned NSS_STATUS_SUCCESS and the next action is +NSS_ACTION_MERGE, a copy of the result buffer is saved for the next pass +through the loop. If on this next pass through the loop the database +returns another instance of a group matching both the group name and GID, +the member list is added to the previous list and it is returned as a +single object. If the following database does not contain the same group, +then the original is copied back into the destination buffer. + +This patch implements merge functionality only for the group database. +For other databases, there is a default implementation that will return +the EINVAL errno if a merge is requested. The merge functionality can be +implemented for other databases at a later time if such is needed. Each +database must provide a unique implementation of the deep-copy and merge +functions. + +If [SUCCESS=merge] is present in nsswitch.conf for a glibc version that +does not support it, glibc will process results up until that operation, +at which time it will return results if it has found them or else will +simply return an error. In practical terms, this ends up behaving like +the remainder of the nsswitch.conf line does not exist. + +== Iterators == +This feature does not modify the iterator functionality from its current +behavior. If getgrnam() or getgrgid() is called, glibc will iterate +through all entries in the `group` line in nsswitch.conf and display the +list of members without attempting to merge them. This is consistent with +the behavior of nss_files where if two separate lines are specified for +the same group in /etc/groups, getgrnam()/getgrgid() will display both. +Clients are already expected to handle this gracefully. + +== No Premature Optimizations == +The following is a list of places that might be eligible for +optimization, but were not overengineered for this initial contribution: + * Any situation where a merge may occur will result in one malloc() of + the same size as the input buffer. + * Any situation where a merge does occur will result in a second + malloc() to hold the list of pointers to member name strings. + * The list of members is simply concatenated together and is not tested + for uniqueness (which is identical to the behavior for nss_files, + which will simply return identical values if they both exist on the + line in the file. This could potentially be optimized to reduce space + usage in the buffer, but it is both complex and computationally + expensive to do so. + +== Testing == +I performed testing by running the getent utility against my newly-built +glibc and configuring /etc/nsswitch.conf with the following entry: +group: group: files [SUCCESS=merge] sss + +In /etc/group I included the line: +wheel:x:10:sgallagh + +I then configured my local SSSD using the id_provider=local to respond +with: +wheel:*:10:localuser,localuser2 + +I then ran `getent group wheel` against the newly-built glibc in +multiple situations and received the expected output as described +above: + * When SSSD was running. + * When SSSD was configured in nsswitch.conf but the daemon was not + running. + * When SSSD was configured in nsswitch.conf but nss_sss.so.2 was not + installed on the system. + * When the order of 'sss' and 'files' was reversed. + * All of the above with the [SUCCESS=merge] removed (to ensure no + regressions). + * All of the above with `getent group 10`. + * All of the above with `getent group` with and without + `enumerate=true` set in SSSD. + * All of the above with and without nscd enabled on the system. + +== NEWS == + +* A new NSS action is added to facilitate large distribution system + administration. The action, MERGE, allows remote user stores like + LDAP to be merged into local user stores like /etc/groups in order + to provide easy to use, updated, and managed sets of merged + credentials. The new action can be used by configuring it in + /etc/nsswitch.conf: + group: files [SUCCESS=merge] nis + Implemented by Stephen Gallagher (Red Hat). + +== ChangeLog == + +2015-12-16 Stephen Gallagher + + [BZ #19072] + * grp/Makefile (headers): Add grp-merge.h + (routines): Add grp-merge. + * grp/getgrgid_r.c: Include grp-merge.h. + (DEEPCOPY_FN): Define. + (MERGE_FN): Define. + * grp/getgrname_r.c: Include grp-merge.h. + (DEEPCOPY_FN): Define. + (MERGE_FN): Define. + * grp/grp-merge.c: New file. + * grp/grp-merge.h: New file. + * manual/nss.texi (Actions in the NSS configuration): Describe + return, continue, and merge. + * nscd/Makefile: Add vpath to find grp-merge.c + (nscd-modules): Add grp-merge. + * nscd/getgrgid_r.c: Include grp/grp-merge.h. + (DEEPCOPY_FN): Define. + (MERGE_FN): Define. + * nscd/getgrnam_r.c: Include grp/grp-merge.h. + (DEEPCOPY_FN): Define. + (MERGE_FN): Define. + * nss/getXXbyYY_r.c [!DEEPCOPY_FN]: Define __copy_einval. + [!MERGE_FN]: Define __merge_einval. + (CHECK_MERGE): Define. + (REENTRANT_NAME): Process merge if do_merge is true. + * nss/getnssent_r.c (__nss_setent): Process NSS_ACTION_MERGE. + (__nss_getent_r): Likewise. + * nss/nsswitch.c (nss_parse_service_list): Likewise. + * nss/nsswitch.h (lookup_actions): Define NSS_ACTION_MERGE. + +Resolves BZ #19072 + +--- +grp/Makefile | 5 +- + grp/getgrgid_r.c | 3 + + grp/getgrnam_r.c | 4 ++ + grp/grp-merge.c | 178 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ + grp/grp-merge.h | 35 +++++++++++ + manual/nss.texi | 46 +++++++++++++- + nscd/Makefile | 5 +- + nscd/getgrgid_r.c | 4 ++ + nscd/getgrnam_r.c | 4 ++ + nss/getXXbyYY_r.c | 105 +++++++++++++++++++++++++++++++- + nss/getnssent_r.c | 34 ++++++++++- + nss/nsswitch.c | 3 + + nss/nsswitch.h | 3 +- + 13 files changed, 419 insertions(+), 10 deletions(-) + create mode 100644 grp/grp-merge.c + create mode 100644 grp/grp-merge.h + +diff --git a/grp/Makefile b/grp/Makefile +index ed8cc2b0564f0e3842cd78f24a4e0788d659bbc4..52af992365268aae8cf8a80cd7216160b1431e84 100644 +--- a/grp/Makefile ++++ b/grp/Makefile +@@ -20,15 +20,16 @@ + # + subdir := grp + + include ../Makeconfig + +-headers := grp.h ++headers := grp.h grp-merge.h + + routines := fgetgrent initgroups setgroups \ + getgrent getgrgid getgrnam putgrent \ +- getgrent_r getgrgid_r getgrnam_r fgetgrent_r ++ getgrent_r getgrgid_r getgrnam_r fgetgrent_r \ ++ grp-merge + + tests := testgrp tst-putgrent + + ifeq (yes,$(build-shared)) + test-srcs := tst_fgetgrent +diff --git a/grp/getgrgid_r.c b/grp/getgrgid_r.c +index 05d4d772d3ef0bfae8f9375387c41310885ce41a..447fa633807deec8f26d654ebeb6386a150d3a37 100644 +--- a/grp/getgrgid_r.c ++++ b/grp/getgrgid_r.c +@@ -16,14 +16,17 @@ + License along with the GNU C Library; if not, see + . */ + + #include + ++#include "grp-merge.h" + + #define LOOKUP_TYPE struct group + #define FUNCTION_NAME getgrgid + #define DATABASE_NAME group + #define ADD_PARAMS gid_t gid + #define ADD_VARIABLES gid + #define BUFLEN NSS_BUFLEN_GROUP ++#define DEEPCOPY_FN __copy_grp ++#define MERGE_FN __merge_grp + + #include +diff --git a/grp/getgrnam_r.c b/grp/getgrnam_r.c +index 0061cb2f7e0bd311d19775e49eb3fdd8a93447f1..c5535f4057ddfc40965f27789c34345045c8bf3b 100644 +--- a/grp/getgrnam_r.c ++++ b/grp/getgrnam_r.c +@@ -16,13 +16,17 @@ + License along with the GNU C Library; if not, see + . */ + + #include + ++#include "grp-merge.h" + + #define LOOKUP_TYPE struct group + #define FUNCTION_NAME getgrnam + #define DATABASE_NAME group + #define ADD_PARAMS const char *name + #define ADD_VARIABLES name + ++#define DEEPCOPY_FN __copy_grp ++#define MERGE_FN __merge_grp ++ + #include +diff --git a/grp/grp-merge.c b/grp/grp-merge.c +new file mode 100644 +index 0000000000000000000000000000000000000000..ca959dbfe403c89d6f3184f2b361b0c6488c9182 +--- /dev/null ++++ b/grp/grp-merge.c +@@ -0,0 +1,178 @@ ++/* Group merging implementation. ++ Copyright (C) 2016 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 "grp-merge.h" ++ ++#define BUFCHECK(size) \ ++ do { \ ++ if (c + size > buflen) \ ++ { \ ++ free (members); \ ++ return ERANGE; \ ++ } \ ++ } while(0) ++ ++int ++__copy_grp (const struct group srcgrp, const size_t buflen, ++ struct group *destgrp, char *destbuf, char **endptr) ++{ ++ size_t i; ++ size_t c = 0; ++ size_t len; ++ size_t memcount; ++ char **members = NULL; ++ ++ /* Copy the GID. */ ++ destgrp->gr_gid = srcgrp.gr_gid; ++ ++ /* Copy the name. */ ++ len = strlen (srcgrp.gr_name) + 1; ++ BUFCHECK (len); ++ memcpy (&destbuf[c], srcgrp.gr_name, len); ++ destgrp->gr_name = &destbuf[c]; ++ c += len; ++ ++ /* Copy the password. */ ++ len = strlen (srcgrp.gr_passwd) + 1; ++ BUFCHECK (len); ++ memcpy (&destbuf[c], srcgrp.gr_passwd, len); ++ destgrp->gr_passwd = &destbuf[c]; ++ c += len; ++ ++ /* Count all of the members. */ ++ for (memcount = 0; srcgrp.gr_mem[memcount]; memcount++) ++ ; ++ ++ /* Allocate a temporary holding area for the pointers to the member ++ contents, including space for a NULL-terminator. */ ++ members = malloc (sizeof (char *) * (memcount + 1)); ++ if (members == NULL) ++ return ENOMEM; ++ ++ /* Copy all of the group members to destbuf and add a pointer to each of ++ them into the 'members' array. */ ++ for (i = 0; srcgrp.gr_mem[i]; i++) ++ { ++ len = strlen (srcgrp.gr_mem[i]) + 1; ++ BUFCHECK (len); ++ memcpy (&destbuf[c], srcgrp.gr_mem[i], len); ++ members[i] = &destbuf[c]; ++ c += len; ++ } ++ members[i] = NULL; ++ ++ /* Copy the pointers from the members array into the buffer and assign them ++ to the gr_mem member of destgrp. */ ++ destgrp->gr_mem = (char **) &destbuf[c]; ++ len = sizeof (char *) * (memcount + 1); ++ BUFCHECK (len); ++ memcpy (&destbuf[c], members, len); ++ c += len; ++ free (members); ++ members = NULL; ++ ++ /* Save the count of members at the end. */ ++ BUFCHECK (sizeof (size_t)); ++ memcpy (&destbuf[c], &memcount, sizeof (size_t)); ++ c += sizeof (size_t); ++ ++ if (endptr) ++ *endptr = destbuf + c; ++ return 0; ++} ++ ++/* Check that the name, GID and passwd fields match, then ++ copy in the gr_mem array. */ ++int ++__merge_grp (struct group *savedgrp, char *savedbuf, char *savedend, ++ size_t buflen, struct group *mergegrp, char *mergebuf) ++{ ++ size_t c, i, len; ++ size_t savedmemcount; ++ size_t memcount; ++ size_t membersize; ++ char **members = NULL; ++ ++ /* We only support merging members of groups with identical names and ++ GID values. If we hit this case, we need to overwrite the current ++ buffer with the saved one (which is functionally equivalent to ++ treating the new lookup as NSS_STATUS NOTFOUND. */ ++ if (mergegrp->gr_gid != savedgrp->gr_gid ++ || strcmp (mergegrp->gr_name, savedgrp->gr_name)) ++ return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL); ++ ++ /* Get the count of group members from the last sizeof (size_t) bytes in the ++ mergegrp buffer. */ ++ savedmemcount = (size_t) *(savedend - sizeof (size_t)); ++ ++ /* Get the count of new members to add. */ ++ for (memcount = 0; mergegrp->gr_mem[memcount]; memcount++) ++ ; ++ ++ /* Create a temporary array to hold the pointers to the member values from ++ both the saved and merge groups. */ ++ membersize = savedmemcount + memcount + 1; ++ members = malloc (sizeof (char *) * membersize); ++ if (members == NULL) ++ return ENOMEM; ++ ++ /* Copy in the existing member pointers from the saved group ++ Note: this is not NULL-terminated yet. */ ++ memcpy (members, savedgrp->gr_mem, sizeof (char *) * savedmemcount); ++ ++ /* Back up into the savedbuf until we get back to the NULL-terminator of the ++ group member list. (This means walking back savedmemcount + 1 (char *) pointers ++ and the member count value. ++ The value of c is going to be the used length of the buffer backed up by ++ the member count and further backed up by the size of the pointers. */ ++ c = savedend - savedbuf ++ - sizeof (size_t) ++ - sizeof (char *) * (savedmemcount + 1); ++ ++ /* Add all the new group members, overwriting the old NULL-terminator while ++ adding the new pointers to the temporary array. */ ++ for (i = 0; mergegrp->gr_mem[i]; i++) ++ { ++ len = strlen (mergegrp->gr_mem[i]) + 1; ++ BUFCHECK (len); ++ memcpy (&savedbuf[c], mergegrp->gr_mem[i], len); ++ members[savedmemcount + i] = &savedbuf[c]; ++ c += len; ++ } ++ /* Add the NULL-terminator. */ ++ members[savedmemcount + memcount] = NULL; ++ ++ /* Copy the member array back into the buffer after the member list and free ++ the member array. */ ++ savedgrp->gr_mem = (char **) &savedbuf[c]; ++ len = sizeof (char *) * membersize; ++ BUFCHECK (len); ++ memcpy (&savedbuf[c], members, len); ++ c += len; ++ ++ free (members); ++ members = NULL; ++ ++ /* Finally, copy the results back into mergebuf, since that's the buffer ++ that we were provided by the caller. */ ++ return __copy_grp (*savedgrp, buflen, mergegrp, mergebuf, NULL); ++} +diff --git a/grp/grp-merge.h b/grp/grp-merge.h +new file mode 100644 +index 0000000000000000000000000000000000000000..59013487d0d907c76521ab504e265077937bfb5e +--- /dev/null ++++ b/grp/grp-merge.h +@@ -0,0 +1,35 @@ ++/* Group merging implementation. ++ Copyright (C) 2016 Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, see ++ . */ ++ ++#ifndef _GRP_MERGE_H ++#define _GRP_MERGE_H 1 ++ ++#include ++ ++/* Duplicate a grp struct (and its members). When no longer needed, the ++ calling function must free(newbuf). */ ++int ++__copy_grp (const struct group srcgrp, const size_t buflen, ++ struct group *destgrp, char *destbuf, char **endptr); ++ ++/* Merge the member lists of two grp structs together. */ ++int ++__merge_grp (struct group *savedgrp, char *savedbuf, char *savedend, ++ size_t buflen, struct group *mergegrp, char *mergebuf); ++ ++#endif /* _GRP_MERGE_H */ +diff --git a/manual/nss.texi b/manual/nss.texi +index 66dcceffe01f225f078e88dd006bb90e80c85723..95e3544bcd97995720e7154ec43df4090154bf4c 100644 +--- a/manual/nss.texi ++++ b/manual/nss.texi +@@ -178,11 +178,11 @@ where + @var{action} @result{} return | continue + @end smallexample + + The case of the keywords is insignificant. The @var{status} + values are the results of a call to a lookup function of a specific +-service. They mean ++service. They mean: + + @ftable @samp + @item success + No error occurred and the wanted entry is returned. The default action + for this is @code{return}. +@@ -202,10 +202,54 @@ The service is temporarily unavailable. This could mean a file is + locked or a server currently cannot accept more connections. The + default action is @code{continue}. + @end ftable + + @noindent ++The @var{action} values mean: ++ ++@ftable @samp ++@item return ++ ++If the status matches, stop the lookup process at this service ++specification. If an entry is available, provide it to the application. ++If an error occurred, report it to the application. In case of a prior ++@samp{merge} action, the data is combined with previous lookup results, ++as explained below. ++ ++@item continue ++ ++If the status matches, proceed with the lookup process at the next ++entry, discarding the result of the current lookup (and any merged ++data). An exception is the @samp{initgroups} database and the ++@samp{success} status, where @samp{continue} acts like @code{merge} ++below. ++ ++@item merge ++ ++Proceed with the lookup process, retaining the current lookup result. ++This action is useful only with the @samp{success} status. If a ++subsequent service lookup succeeds and has a matching @samp{return} ++specification, the results are merged, the lookup process ends, and the ++merged results are returned to the application. If the following service ++has a matching @samp{merge} action, the lookup process continues, ++retaining the combined data from this and any previous lookups. ++ ++After a @code{merge} action, errors from subsequent lookups are ignored, ++and the data gathered so far will be returned. ++ ++The @samp{merge} only applies to the @samp{success} status. It is ++currently implemented for the @samp{group} database and its group ++members field, @samp{gr_mem}. If specified for other databases, it ++causes the lookup to fail (if the @var{status} matches). ++ ++When processing @samp{merge} for @samp{group} membership, the group GID ++and name must be identical for both entries. If only one or the other is ++a match, the behavior is undefined. ++ ++@end ftable ++ ++@noindent + If we have a line like + + @smallexample + ethers: nisplus [NOTFOUND=return] db files + @end smallexample +diff --git a/nscd/Makefile b/nscd/Makefile +index e1a1aa92fc699aa132f7192da49f698a078e5910..3e6895573ae33221c728617f4c95bb3e8c5d5c47 100644 +--- a/nscd/Makefile ++++ b/nscd/Makefile +@@ -29,16 +29,19 @@ aux := nscd_helper + endif + + # To find xmalloc.c + vpath %.c ../locale/programs + ++# To find grp-merge.c ++vpath %.c ../grp ++ + nscd-modules := nscd connections pwdcache getpwnam_r getpwuid_r grpcache \ + getgrnam_r getgrgid_r hstcache gethstbyad_r gethstbynm3_r \ + getsrvbynm_r getsrvbypt_r servicescache \ + dbg_log nscd_conf nscd_stat cache mem nscd_setup_thread \ + xmalloc xstrdup aicache initgrcache gai res_hconf \ +- netgroupcache ++ netgroupcache grp-merge + + ifeq ($(build-nscd)$(have-thread-library),yesyes) + + others += nscd + others-pie += nscd +diff --git a/nscd/getgrgid_r.c b/nscd/getgrgid_r.c +index fe5bda424169d56f642f125ef1f2df77a84de221..25de4a3b0b74841c44844a0541cf4d2365b22515 100644 +--- a/nscd/getgrgid_r.c ++++ b/nscd/getgrgid_r.c +@@ -15,17 +15,21 @@ + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + + #include + ++#include "grp/grp-merge.h" + + #define LOOKUP_TYPE struct group + #define FUNCTION_NAME getgrgid + #define DATABASE_NAME group + #define ADD_PARAMS gid_t gid + #define ADD_VARIABLES gid + #define BUFLEN NSS_BUFLEN_GROUP + ++#define DEEPCOPY_FN __copy_grp ++#define MERGE_FN __merge_grp ++ + /* We are nscd, so we don't want to be talking to ourselves. */ + #undef USE_NSCD + + #include +diff --git a/nscd/getgrnam_r.c b/nscd/getgrnam_r.c +index 5ec56877f5798ca34c2e0074d5093cc22b6d58dc..386d66c5832ffee68e95195f6e34b723f41d0984 100644 +--- a/nscd/getgrnam_r.c ++++ b/nscd/getgrnam_r.c +@@ -15,16 +15,20 @@ + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + + #include + ++#include "grp/grp-merge.h" + + #define LOOKUP_TYPE struct group + #define FUNCTION_NAME getgrnam + #define DATABASE_NAME group + #define ADD_PARAMS const char *name + #define ADD_VARIABLES name + ++#define DEEPCOPY_FN __copy_grp ++#define MERGE_FN __merge_grp ++ + /* We are nscd, so we don't want to be talking to ourselves. */ + #undef USE_NSCD + + #include +diff --git a/nss/getXXbyYY_r.c b/nss/getXXbyYY_r.c +index 198f8cfebd51be9c738f03f950f093b0855ab3cb..5f49ae8b828fce6dc30c7ccc9606319bffc52d5b 100644 +--- a/nss/getXXbyYY_r.c ++++ b/nss/getXXbyYY_r.c +@@ -129,10 +129,52 @@ + # define AF_VAL af + #else + # define AF_VAL AF_INET + #endif + ++ ++/* Set defaults for merge functions that haven't been defined. */ ++#ifndef DEEPCOPY_FN ++static inline int ++__copy_einval (LOOKUP_TYPE a, ++ const size_t b, ++ LOOKUP_TYPE *c, ++ char *d, ++ char **e) ++{ ++ return EINVAL; ++} ++# define DEEPCOPY_FN __copy_einval ++#endif ++ ++#ifndef MERGE_FN ++static inline int ++__merge_einval (LOOKUP_TYPE *a, ++ char *b, ++ char *c, ++ size_t d, ++ LOOKUP_TYPE *e, ++ char *f) ++{ ++ return EINVAL; ++} ++# define MERGE_FN __merge_einval ++#endif ++ ++#define CHECK_MERGE(err, status) \ ++do { \ ++ if (err) \ ++ { \ ++ __set_errno (err); \ ++ if (err == ERANGE) \ ++ status = NSS_STATUS_TRYAGAIN; \ ++ else \ ++ status = NSS_STATUS_UNAVAIL; \ ++ break; \ ++ } \ ++} while(0) ++ + /* Type of the lookup function we need here. */ + typedef enum nss_status (*lookup_function) (ADD_PARAMS, LOOKUP_TYPE *, char *, + size_t, int * H_ERRNO_PARM + EXTRA_PARAMS); + +@@ -150,17 +192,20 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer, + { + static bool startp_initialized; + static service_user *startp; + static lookup_function start_fct; + service_user *nip; ++ int do_merge = 0; ++ LOOKUP_TYPE mergegrp; ++ char *mergebuf = NULL; ++ char *endptr = NULL; + union + { + lookup_function l; + void *ptr; + } fct; +- +- int no_more; ++ int no_more, err; + enum nss_status status = NSS_STATUS_UNAVAIL; + #ifdef USE_NSCD + int nscd_status; + #endif + #ifdef NEED_H_ERRNO +@@ -276,13 +321,69 @@ INTERNAL (REENTRANT_NAME) (ADD_PARAMS, LOOKUP_TYPE *resbuf, char *buffer, + && *h_errnop == NETDB_INTERNAL + #endif + && errno == ERANGE) + break; + ++ if (do_merge) ++ { ++ ++ if (status == NSS_STATUS_SUCCESS) ++ { ++ /* The previous loop saved a buffer for merging. ++ Perform the merge now. */ ++ err = MERGE_FN (&mergegrp, mergebuf, endptr, buflen, resbuf, ++ buffer); ++ CHECK_MERGE (err,status); ++ do_merge = 0; ++ } ++ else ++ { ++ /* If the result wasn't SUCCESS, copy the saved buffer back ++ into the result buffer and set the status back to ++ NSS_STATUS_SUCCESS to match the previous pass through the loop. ++ * If the next action is CONTINUE, it will overwrite the value ++ currently in the buffer and return the new value. ++ * If the next action is RETURN, we'll return the previously- ++ acquired values. ++ * If the next action is MERGE, then it will be added to the buffer ++ saved from the previous source. */ ++ err = DEEPCOPY_FN (mergegrp, buflen, resbuf, buffer, NULL); ++ CHECK_MERGE (err, status); ++ status = NSS_STATUS_SUCCESS; ++ } ++ } ++ ++ /* If we were are configured to merge this value with the next one, ++ save the current value of the group struct. */ ++ if (nss_next_action (nip, status) == NSS_ACTION_MERGE ++ && status == NSS_STATUS_SUCCESS) ++ { ++ /* Copy the current values into a buffer to be merged with the next ++ set of retrieved values. */ ++ if (!mergebuf) ++ { ++ /* Only allocate once and reuse it for as many merges as we need ++ to perform. */ ++ mergebuf = malloc (buflen); ++ if (!mergebuf) ++ { ++ __set_errno (ENOMEM); ++ status = NSS_STATUS_UNAVAIL; ++ break; ++ } ++ } ++ ++ err = DEEPCOPY_FN (*resbuf, buflen, &mergegrp, mergebuf, &endptr); ++ CHECK_MERGE (err, status); ++ do_merge = 1; ++ } ++ + no_more = __nss_next2 (&nip, REENTRANT_NAME_STRING, + REENTRANT2_NAME_STRING, &fct.ptr, status, 0); + } ++ free(mergebuf); ++ mergebuf = NULL; + + #ifdef HANDLE_DIGITS_DOTS + done: + #endif + *result = status == NSS_STATUS_SUCCESS ? resbuf : NULL; +diff --git a/nss/getnssent_r.c b/nss/getnssent_r.c +index f5b903671ca53ccad108eeb4e49ea40a45fa5cdf..c0743436f661d4d83045a6353b49291a4c0f220b 100644 +--- a/nss/getnssent_r.c ++++ b/nss/getnssent_r.c +@@ -77,11 +77,25 @@ __nss_setent (const char *func_name, db_lookup_function lookup_fct, + if (stayopen_tmp) + status = DL_CALL_FCT (fct.f, (*stayopen_tmp)); + else + status = DL_CALL_FCT (fct.f, (0)); + +- no_more = __nss_next2 (nip, func_name, NULL, &fct.ptr, status, 0); ++ if (nss_next_action (*nip, status) == NSS_ACTION_MERGE) ++ { ++ /* This is a special-case. When [SUCCESS=merge] is in play, ++ _nss_next2() will skip to the next database. Due to the ++ implementation of that function, we can't know whether we're ++ in an enumeration or an individual lookup, which behaves ++ differently with regards to merging. We'll treat SUCCESS as ++ an indication to start the enumeration at this database. */ ++ no_more = 1; ++ } ++ else ++ { ++ no_more = __nss_next2 (nip, func_name, NULL, &fct.ptr, status, 0); ++ } ++ + if (is_last_nip) + *last_nip = *nip; + } + + if (stayopen_tmp) +@@ -173,12 +187,26 @@ __nss_getent_r (const char *getent_func_name, + && errno == ERANGE) + break; + + do + { +- no_more = __nss_next2 (nip, getent_func_name, NULL, &fct.ptr, +- status, 0); ++ if (status == NSS_STATUS_SUCCESS ++ && nss_next_action (*nip, status) == NSS_ACTION_MERGE) ++ { ++ /* This is a special-case. When [SUCCESS=merge] is in play, ++ _nss_next2() will skip to the next database. Due to the ++ implementation of that function, we can't know whether we're ++ in an enumeration or an individual lookup, which behaves ++ differently with regards to merging. We'll treat SUCCESS as ++ an indication to return the results here. */ ++ no_more = 1; ++ } ++ else ++ { ++ no_more = __nss_next2 (nip, getent_func_name, NULL, &fct.ptr, ++ status, 0); ++ } + + if (is_last_nip) + *last_nip = *nip; + + if (! no_more) +diff --git a/nss/nsswitch.c b/nss/nsswitch.c +index faf9d1a0d5680aa79e88b2dfeea18da371c336fb..f8f60ba05aad9a571f928a3ea7d3b14f908ccb2d 100644 +--- a/nss/nsswitch.c ++++ b/nss/nsswitch.c +@@ -710,10 +710,13 @@ nss_parse_service_list (const char *line) + if (line - name == 6 && __strncasecmp (name, "RETURN", 6) == 0) + action = NSS_ACTION_RETURN; + else if (line - name == 8 + && __strncasecmp (name, "CONTINUE", 8) == 0) + action = NSS_ACTION_CONTINUE; ++ else if (line - name == 5 ++ && __strncasecmp (name, "MERGE", 5) == 0) ++ action = NSS_ACTION_MERGE; + else + goto finish; + + if (not) + { +diff --git a/nss/nsswitch.h b/nss/nsswitch.h +index a5318fa82be43c8314807ad76de231e572e91c06..5bc2de3b1d82978102ac7a129c0ba5b7eb3cfd25 100644 +--- a/nss/nsswitch.h ++++ b/nss/nsswitch.h +@@ -30,11 +30,12 @@ + + /* Actions performed after lookup finished. */ + typedef enum + { + NSS_ACTION_CONTINUE, +- NSS_ACTION_RETURN ++ NSS_ACTION_RETURN, ++ NSS_ACTION_MERGE + } lookup_actions; + + + typedef struct service_library + { diff --git a/glibc.spec b/glibc.spec index bcab66c..5512d2c 100644 --- a/glibc.spec +++ b/glibc.spec @@ -1,6 +1,6 @@ %define glibcsrcdir glibc-2.22-621-g90c400b %define glibcversion 2.22.90 -%define glibcrelease 27%{?dist} +%define glibcrelease 28%{?dist} # Pre-release tarballs are pulled in from git using a command that is # effectively: # @@ -288,6 +288,9 @@ Patch2031: glibc-rh1070416.patch Patch2033: glibc-aarch64-tls-fixes.patch Patch2034: glibc-aarch64-workaround-nzcv-clobber-in-tlsdesc.patch +# Group Merge Patch: +Patch2035: glibc-nsswitch-Add-group-merging-support.patch + ############################################################################## # # Benchmark comparison patches. @@ -679,6 +682,7 @@ cat /proc/meminfo %patch0057 -p1 %patch0058 -p1 %patch0059 -p1 +%patch2035 -p1 ############################################################################## # %%prep - Additional prep required... @@ -1937,6 +1941,9 @@ rm -f *.filelist* %endif %changelog +* Wed Jan 13 2016 Carlos O'Donell - 2.22.90-28 +- Add group merging support for distributed management (#1146822). + * Tue Jan 12 2016 Carlos O'Donell - 2.22.90-27 - Remove 32-bit POWER support. - Add 64-bit POWER7 BE and 64-bit POWER8 BE optimized libraries.