root/maint/gnulib/lib/mgetgroups.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. realloc_groupbuf
  2. mgetgroups

   1 /* mgetgroups.c -- return a list of the groups a user or current process is in
   2 
   3    Copyright (C) 2007-2021 Free Software Foundation, Inc.
   4 
   5    This file is free software: you can redistribute it and/or modify
   6    it under the terms of the GNU Lesser General Public License as
   7    published by the Free Software Foundation; either version 2.1 of the
   8    License, or (at your option) any later version.
   9 
  10    This file is distributed in the hope that it will be useful,
  11    but WITHOUT ANY WARRANTY; without even the implied warranty of
  12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13    GNU Lesser General Public License for more details.
  14 
  15    You should have received a copy of the GNU Lesser General Public License
  16    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
  17 
  18 /* Extracted from coreutils' src/id.c. */
  19 
  20 #include <config.h>
  21 
  22 #include "mgetgroups.h"
  23 
  24 #include <stdlib.h>
  25 #include <unistd.h>
  26 #include <stdint.h>
  27 #include <string.h>
  28 #include <errno.h>
  29 #if HAVE_GETGROUPLIST
  30 # include <grp.h>
  31 #endif
  32 
  33 #include "getugroups.h"
  34 #include "xalloc-oversized.h"
  35 
  36 /* Work around an incompatibility of OS X 10.11: getgrouplist
  37    accepts int *, not gid_t *, and int and gid_t differ in sign.  */
  38 #if 4 < __GNUC__ + (3 <= __GNUC_MINOR__) || defined __clang__
  39 # pragma GCC diagnostic ignored "-Wpointer-sign"
  40 #endif
  41 
  42 static gid_t *
  43 realloc_groupbuf (gid_t *g, size_t num)
     /* [previous][next][first][last][top][bottom][index][help] */
  44 {
  45   if (xalloc_oversized (num, sizeof *g))
  46     {
  47       errno = ENOMEM;
  48       return NULL;
  49     }
  50 
  51   return realloc (g, num * sizeof *g);
  52 }
  53 
  54 /* Like getugroups, but store the result in malloc'd storage.
  55    Set *GROUPS to the malloc'd list of all group IDs of which USERNAME
  56    is a member.  If GID is not -1, store it first.  GID should be the
  57    group ID (pw_gid) obtained from getpwuid, in case USERNAME is not
  58    listed in the groups database (e.g., /etc/groups).  If USERNAME is
  59    NULL, store the supplementary groups of the current process, and GID
  60    should be -1 or the effective group ID (getegid).  Upon failure,
  61    don't modify *GROUPS, set errno, and return -1.  Otherwise, return
  62    the number of groups.  The resulting list may contain duplicates,
  63    but adjacent members will be distinct.  */
  64 
  65 int
  66 mgetgroups (char const *username, gid_t gid, gid_t **groups)
     /* [previous][next][first][last][top][bottom][index][help] */
  67 {
  68   int max_n_groups;
  69   int ng;
  70   gid_t *g;
  71 
  72 #if HAVE_GETGROUPLIST
  73   /* We prefer to use getgrouplist if available, because it has better
  74      performance characteristics.
  75 
  76      In glibc 2.3.2, getgrouplist is buggy.  If you pass a zero as the
  77      length of the output buffer, getgrouplist will still write to the
  78      buffer.  Contrary to what some versions of the getgrouplist
  79      manpage say, this doesn't happen with nonzero buffer sizes.
  80      Therefore our usage here just avoids a zero sized buffer.  */
  81   if (username)
  82     {
  83       enum { N_GROUPS_INIT = 10 };
  84       max_n_groups = N_GROUPS_INIT;
  85 
  86       g = realloc_groupbuf (NULL, max_n_groups);
  87       if (g == NULL)
  88         return -1;
  89 
  90       while (1)
  91         {
  92           gid_t *h;
  93           int last_n_groups = max_n_groups;
  94 
  95           /* getgrouplist updates max_n_groups to num required.  */
  96           ng = getgrouplist (username, gid, g, &max_n_groups);
  97 
  98           /* Some systems (like Darwin) have a bug where they
  99              never increase max_n_groups.  */
 100           if (ng < 0 && last_n_groups == max_n_groups)
 101             max_n_groups *= 2;
 102 
 103           if ((h = realloc_groupbuf (g, max_n_groups)) == NULL)
 104             {
 105               free (g);
 106               return -1;
 107             }
 108           g = h;
 109 
 110           if (0 <= ng)
 111             {
 112               *groups = g;
 113               /* On success some systems just return 0 from getgrouplist,
 114                  so return max_n_groups rather than ng.  */
 115               return max_n_groups;
 116             }
 117         }
 118     }
 119   /* else no username, so fall through and use getgroups. */
 120 #endif
 121 
 122   max_n_groups = (username
 123                   ? getugroups (0, NULL, username, gid)
 124                   : getgroups (0, NULL));
 125 
 126   /* If we failed to count groups because there is no supplemental
 127      group support, then return an array containing just GID.
 128      Otherwise, we fail for the same reason.  */
 129   if (max_n_groups < 0)
 130     {
 131       if (errno == ENOSYS && (g = realloc_groupbuf (NULL, 1)))
 132         {
 133           *groups = g;
 134           *g = gid;
 135           return gid != (gid_t) -1;
 136         }
 137       return -1;
 138     }
 139 
 140   if (max_n_groups == 0 || (!username && gid != (gid_t) -1))
 141     max_n_groups++;
 142   g = realloc_groupbuf (NULL, max_n_groups);
 143   if (g == NULL)
 144     return -1;
 145 
 146   ng = (username
 147         ? getugroups (max_n_groups, g, username, gid)
 148         : getgroups (max_n_groups - (gid != (gid_t) -1),
 149                                 g + (gid != (gid_t) -1)));
 150 
 151   if (ng < 0)
 152     {
 153       /* Failure is unexpected, but handle it anyway.  */
 154       free (g);
 155       return -1;
 156     }
 157 
 158   if (!username && gid != (gid_t) -1)
 159     {
 160       *g = gid;
 161       ng++;
 162     }
 163   *groups = g;
 164 
 165   /* Reduce the number of duplicates.  On some systems, getgroups
 166      returns the effective gid twice: once as the first element, and
 167      once in its position within the supplementary groups.  On other
 168      systems, getgroups does not return the effective gid at all,
 169      which is why we provide a GID argument.  Meanwhile, the GID
 170      argument, if provided, is typically any member of the
 171      supplementary groups, and not necessarily the effective gid.  So,
 172      the most likely duplicates are the first element with an
 173      arbitrary other element, or pair-wise duplication between the
 174      first and second elements returned by getgroups.  It is possible
 175      that this O(n) pass will not remove all duplicates, but it is not
 176      worth the effort to slow down to an O(n log n) algorithm that
 177      sorts the array in place, nor the extra memory needed for
 178      duplicate removal via an O(n) hash-table.  Hence, this function
 179      is only documented as guaranteeing no pair-wise duplicates,
 180      rather than returning the minimal set.  */
 181   if (1 < ng)
 182     {
 183       gid_t first = *g;
 184       gid_t *next;
 185       gid_t *groups_end = g + ng;
 186 
 187       for (next = g + 1; next < groups_end; next++)
 188         {
 189           if (*next == first || *next == *g)
 190             ng--;
 191           else
 192             *++g = *next;
 193         }
 194     }
 195 
 196   return ng;
 197 }

/* [previous][next][first][last][top][bottom][index][help] */