Blame grp/initgroups.c

Packit 6c4009
/* Copyright (C) 1989, 1991-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
Packit 6c4009
   The GNU C Library is free software; you can redistribute it and/or
Packit 6c4009
   modify it under the terms of the GNU Lesser General Public
Packit 6c4009
   License as published by the Free Software Foundation; either
Packit 6c4009
   version 2.1 of the License, or (at your option) any later version.
Packit 6c4009
Packit 6c4009
   The GNU C Library is distributed in the hope that it will be useful,
Packit 6c4009
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 6c4009
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 6c4009
   Lesser General Public License for more details.
Packit 6c4009
Packit 6c4009
   You should have received a copy of the GNU Lesser General Public
Packit 6c4009
   License along with the GNU C Library; if not, see
Packit 6c4009
   <http://www.gnu.org/licenses/>.  */
Packit 6c4009
Packit 6c4009
#include <assert.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <grp.h>
Packit 6c4009
#include <limits.h>
Packit 6c4009
#include <stdlib.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <sys/types.h>
Packit 6c4009
#include <nsswitch.h>
Packit 6c4009
#include <scratch_buffer.h>
Packit 6c4009
#include <config.h>
Packit 6c4009
Packit 6c4009
#include "../nscd/nscd-client.h"
Packit 6c4009
#include "../nscd/nscd_proto.h"
Packit 6c4009
Packit 6c4009
#ifdef LINK_OBSOLETE_NSL
Packit 6c4009
# define DEFAULT_CONFIG "compat [NOTFOUND=return] files"
Packit 6c4009
#else
Packit 6c4009
# define DEFAULT_CONFIG "files"
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
/* Type of the lookup function.  */
Packit 6c4009
typedef enum nss_status (*initgroups_dyn_function) (const char *, gid_t,
Packit 6c4009
						    long int *, long int *,
Packit 6c4009
						    gid_t **, long int, int *);
Packit 6c4009
Packit 6c4009
static bool use_initgroups_entry;
Packit 6c4009
Packit 6c4009
Packit 6c4009
#include "compat-initgroups.c"
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
internal_getgrouplist (const char *user, gid_t group, long int *size,
Packit 6c4009
		       gid_t **groupsp, long int limit)
Packit 6c4009
{
Packit 6c4009
#ifdef USE_NSCD
Packit 6c4009
  if (__nss_not_use_nscd_group > 0
Packit 6c4009
      && ++__nss_not_use_nscd_group > NSS_NSCD_RETRY)
Packit 6c4009
    __nss_not_use_nscd_group = 0;
Packit 6c4009
  if (!__nss_not_use_nscd_group
Packit 6c4009
      && !__nss_database_custom[NSS_DBSIDX_group])
Packit 6c4009
    {
Packit 6c4009
      int n = __nscd_getgrouplist (user, group, size, groupsp, limit);
Packit 6c4009
      if (n >= 0)
Packit 6c4009
	return n;
Packit 6c4009
Packit 6c4009
      /* nscd is not usable.  */
Packit 6c4009
      __nss_not_use_nscd_group = 1;
Packit 6c4009
    }
Packit 6c4009
#endif
Packit 6c4009
Packit 6c4009
  enum nss_status status = NSS_STATUS_UNAVAIL;
Packit 6c4009
  int no_more = 0;
Packit 6c4009
Packit 6c4009
  /* Never store more than the starting *SIZE number of elements.  */
Packit 6c4009
  assert (*size > 0);
Packit 6c4009
  (*groupsp)[0] = group;
Packit 6c4009
  /* Start is one, because we have the first group as parameter.  */
Packit 6c4009
  long int start = 1;
Packit 6c4009
Packit 6c4009
  if (__nss_initgroups_database == NULL)
Packit 6c4009
    {
Packit 6c4009
      if (__nss_database_lookup ("initgroups", NULL, "",
Packit 6c4009
				 &__nss_initgroups_database) < 0)
Packit 6c4009
	{
Packit 6c4009
	  if (__nss_group_database == NULL)
Packit 6c4009
	    no_more = __nss_database_lookup ("group", NULL, DEFAULT_CONFIG,
Packit 6c4009
					     &__nss_group_database);
Packit 6c4009
Packit 6c4009
	  __nss_initgroups_database = __nss_group_database;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	use_initgroups_entry = true;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    /* __nss_initgroups_database might have been set through
Packit 6c4009
       __nss_configure_lookup in which case use_initgroups_entry was
Packit 6c4009
       not set here.  */
Packit 6c4009
    use_initgroups_entry = __nss_initgroups_database != __nss_group_database;
Packit 6c4009
Packit 6c4009
  service_user *nip = __nss_initgroups_database;
Packit 6c4009
  while (! no_more)
Packit 6c4009
    {
Packit 6c4009
      long int prev_start = start;
Packit 6c4009
Packit 6c4009
      initgroups_dyn_function fct = __nss_lookup_function (nip,
Packit 6c4009
							   "initgroups_dyn");
Packit 6c4009
      if (fct == NULL)
Packit 6c4009
	status = compat_call (nip, user, group, &start, size, groupsp,
Packit 6c4009
			      limit, &errno);
Packit 6c4009
      else
Packit 6c4009
	status = DL_CALL_FCT (fct, (user, group, &start, size, groupsp,
Packit 6c4009
				    limit, &errno));
Packit 6c4009
Packit 6c4009
      /* Remove duplicates.  */
Packit 6c4009
      long int cnt = prev_start;
Packit 6c4009
      while (cnt < start)
Packit 6c4009
	{
Packit 6c4009
	  long int inner;
Packit 6c4009
	  for (inner = 0; inner < prev_start; ++inner)
Packit 6c4009
	    if ((*groupsp)[inner] == (*groupsp)[cnt])
Packit 6c4009
	      break;
Packit 6c4009
Packit 6c4009
	  if (inner < prev_start)
Packit 6c4009
	    (*groupsp)[cnt] = (*groupsp)[--start];
Packit 6c4009
	  else
Packit 6c4009
	    ++cnt;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* This is really only for debugging.  */
Packit 6c4009
      if (NSS_STATUS_TRYAGAIN > status || status > NSS_STATUS_RETURN)
Packit 6c4009
	__libc_fatal ("illegal status in internal_getgrouplist");
Packit 6c4009
Packit 6c4009
      /* For compatibility reason we will continue to look for more
Packit 6c4009
	 entries using the next service even though data has already
Packit 6c4009
	 been found if the nsswitch.conf file contained only a 'groups'
Packit 6c4009
	 line and no 'initgroups' line.  If the latter is available
Packit 6c4009
	 we always respect the status.  This means that the default
Packit 6c4009
	 for successful lookups is to return.  */
Packit 6c4009
      if ((use_initgroups_entry || status != NSS_STATUS_SUCCESS)
Packit 6c4009
	  && nss_next_action (nip, status) == NSS_ACTION_RETURN)
Packit 6c4009
	 break;
Packit 6c4009
Packit 6c4009
      if (nip->next == NULL)
Packit 6c4009
	no_more = -1;
Packit 6c4009
      else
Packit 6c4009
	nip = nip->next;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return start;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Store at most *NGROUPS members of the group set for USER into
Packit 6c4009
   *GROUPS.  Also include GROUP.  The actual number of groups found is
Packit 6c4009
   returned in *NGROUPS.  Return -1 if the if *NGROUPS is too small.  */
Packit 6c4009
int
Packit 6c4009
getgrouplist (const char *user, gid_t group, gid_t *groups, int *ngroups)
Packit 6c4009
{
Packit 6c4009
  long int size = MAX (1, *ngroups);
Packit 6c4009
Packit 6c4009
  gid_t *newgroups = (gid_t *) malloc (size * sizeof (gid_t));
Packit 6c4009
  if (__glibc_unlikely (newgroups == NULL))
Packit 6c4009
    /* No more memory.  */
Packit 6c4009
    // XXX This is wrong.  The user provided memory, we have to use
Packit 6c4009
    // XXX it.  The internal functions must be called with the user
Packit 6c4009
    // XXX provided buffer and not try to increase the size if it is
Packit 6c4009
    // XXX too small.  For initgroups a flag could say: increase size.
Packit 6c4009
    return -1;
Packit 6c4009
Packit 6c4009
  int total = internal_getgrouplist (user, group, &size, &newgroups, -1);
Packit 6c4009
Packit 6c4009
  memcpy (groups, newgroups, MIN (*ngroups, total) * sizeof (gid_t));
Packit 6c4009
Packit 6c4009
  free (newgroups);
Packit 6c4009
Packit 6c4009
  int retval = total > *ngroups ? -1 : total;
Packit 6c4009
  *ngroups = total;
Packit 6c4009
Packit 6c4009
  return retval;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
nss_interface_function (getgrouplist)
Packit 6c4009
Packit 6c4009
/* Initialize the group set for the current user
Packit 6c4009
   by reading the group database and using all groups
Packit 6c4009
   of which USER is a member.  Also include GROUP.  */
Packit 6c4009
int
Packit 6c4009
initgroups (const char *user, gid_t group)
Packit 6c4009
{
Packit 6c4009
#if defined NGROUPS_MAX && NGROUPS_MAX == 0
Packit 6c4009
Packit 6c4009
  /* No extra groups allowed.  */
Packit 6c4009
  return 0;
Packit 6c4009
Packit 6c4009
#else
Packit 6c4009
Packit 6c4009
  long int size;
Packit 6c4009
  gid_t *groups;
Packit 6c4009
  int ngroups;
Packit 6c4009
  int result;
Packit 6c4009
Packit 6c4009
 /* We always use sysconf even if NGROUPS_MAX is defined.  That way, the
Packit 6c4009
     limit can be raised in the kernel configuration without having to
Packit 6c4009
     recompile libc.  */
Packit 6c4009
  long int limit = __sysconf (_SC_NGROUPS_MAX);
Packit 6c4009
Packit 6c4009
  if (limit > 0)
Packit 6c4009
    /* We limit the size of the intially allocated array.  */
Packit 6c4009
    size = MIN (limit, 64);
Packit 6c4009
  else
Packit 6c4009
    /* No fixed limit on groups.  Pick a starting buffer size.  */
Packit 6c4009
    size = 16;
Packit 6c4009
Packit 6c4009
  groups = (gid_t *) malloc (size * sizeof (gid_t));
Packit 6c4009
  if (__glibc_unlikely (groups == NULL))
Packit 6c4009
    /* No more memory.  */
Packit 6c4009
    return -1;
Packit 6c4009
Packit 6c4009
  ngroups = internal_getgrouplist (user, group, &size, &groups, limit);
Packit 6c4009
Packit 6c4009
  /* Try to set the maximum number of groups the kernel can handle.  */
Packit 6c4009
  do
Packit 6c4009
    result = setgroups (ngroups, groups);
Packit 6c4009
  while (result == -1 && errno == EINVAL && --ngroups > 0);
Packit 6c4009
Packit 6c4009
  free (groups);
Packit 6c4009
Packit 6c4009
  return result;
Packit 6c4009
#endif
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
nss_interface_function (initgroups)