Blame grp/initgroups.c

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