Blame nss/nss_compat/compat-initgroups.c

Packit 6c4009
/* Copyright (C) 1998-2018 Free Software Foundation, Inc.
Packit 6c4009
   This file is part of the GNU C Library.
Packit 6c4009
   Contributed by Thorsten Kukuk <kukuk@suse.de>, 1998.
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 <ctype.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <fcntl.h>
Packit 6c4009
#include <grp.h>
Packit 6c4009
#include <nss.h>
Packit 6c4009
#include <stdio_ext.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <unistd.h>
Packit 6c4009
#include <sys/param.h>
Packit 6c4009
#include <nsswitch.h>
Packit 6c4009
#include <libc-lock.h>
Packit 6c4009
#include <kernel-features.h>
Packit 6c4009
#include <scratch_buffer.h>
Packit 6c4009
Packit 6c4009
static service_user *ni;
Packit 6c4009
/* Type of the lookup function.  */
Packit 6c4009
static enum nss_status (*nss_initgroups_dyn) (const char *, gid_t,
Packit 6c4009
					      long int *, long int *,
Packit 6c4009
					      gid_t **, long int, int *);
Packit 6c4009
static enum nss_status (*nss_getgrnam_r) (const char *name,
Packit 6c4009
					  struct group * grp, char *buffer,
Packit 6c4009
					  size_t buflen, int *errnop);
Packit 6c4009
static enum nss_status (*nss_getgrgid_r) (gid_t gid, struct group * grp,
Packit 6c4009
					  char *buffer, size_t buflen,
Packit 6c4009
					  int *errnop);
Packit 6c4009
static enum nss_status (*nss_setgrent) (int stayopen);
Packit 6c4009
static enum nss_status (*nss_getgrent_r) (struct group * grp, char *buffer,
Packit 6c4009
					  size_t buflen, int *errnop);
Packit 6c4009
static enum nss_status (*nss_endgrent) (void);
Packit 6c4009
Packit 6c4009
/* Protect global state against multiple changers.  */
Packit 6c4009
__libc_lock_define_initialized (static, lock)
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Get the declaration of the parser function.  */
Packit 6c4009
#define ENTNAME grent
Packit 6c4009
#define STRUCTURE group
Packit 6c4009
#define EXTERN_PARSER
Packit 6c4009
#include <nss/nss_files/files-parse.c>
Packit 6c4009
Packit 6c4009
/* Structure for remembering -group members ... */
Packit 6c4009
#define BLACKLIST_INITIAL_SIZE 512
Packit 6c4009
#define BLACKLIST_INCREMENT 256
Packit 6c4009
struct blacklist_t
Packit 6c4009
{
Packit 6c4009
  char *data;
Packit 6c4009
  int current;
Packit 6c4009
  int size;
Packit 6c4009
};
Packit 6c4009
Packit 6c4009
struct ent_t
Packit 6c4009
{
Packit 6c4009
  bool files;
Packit 6c4009
  bool need_endgrent;
Packit 6c4009
  bool skip_initgroups_dyn;
Packit 6c4009
  FILE *stream;
Packit 6c4009
  struct blacklist_t blacklist;
Packit 6c4009
};
Packit 6c4009
typedef struct ent_t ent_t;
Packit 6c4009
Packit 6c4009
/* Prototypes for local functions.  */
Packit 6c4009
static void blacklist_store_name (const char *, ent_t *);
Packit 6c4009
static bool in_blacklist (const char *, int, ent_t *);
Packit 6c4009
Packit 6c4009
/* Initialize the NSS interface/functions. The calling function must
Packit 6c4009
   hold the lock.  */
Packit 6c4009
static void
Packit 6c4009
init_nss_interface (void)
Packit 6c4009
{
Packit 6c4009
  __libc_lock_lock (lock);
Packit 6c4009
Packit 6c4009
  /* Retest.  */
Packit 6c4009
  if (ni == NULL
Packit Service 3b0880
      && __nss_database_lookup ("group_compat", NULL, "nis", &ni) >= 0)
Packit 6c4009
    {
Packit 6c4009
      nss_initgroups_dyn = __nss_lookup_function (ni, "initgroups_dyn");
Packit 6c4009
      nss_getgrnam_r = __nss_lookup_function (ni, "getgrnam_r");
Packit 6c4009
      nss_getgrgid_r = __nss_lookup_function (ni, "getgrgid_r");
Packit 6c4009
      nss_setgrent = __nss_lookup_function (ni, "setgrent");
Packit 6c4009
      nss_getgrent_r = __nss_lookup_function (ni, "getgrent_r");
Packit 6c4009
      nss_endgrent = __nss_lookup_function (ni, "endgrent");
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __libc_lock_unlock (lock);
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static enum nss_status
Packit 6c4009
internal_setgrent (ent_t *ent)
Packit 6c4009
{
Packit 6c4009
  enum nss_status status = NSS_STATUS_SUCCESS;
Packit 6c4009
Packit 6c4009
  ent->files = true;
Packit 6c4009
Packit 6c4009
  if (ni == NULL)
Packit 6c4009
    init_nss_interface ();
Packit 6c4009
Packit 6c4009
  if (ent->blacklist.data != NULL)
Packit 6c4009
    {
Packit 6c4009
      ent->blacklist.current = 1;
Packit 6c4009
      ent->blacklist.data[0] = '|';
Packit 6c4009
      ent->blacklist.data[1] = '\0';
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    ent->blacklist.current = 0;
Packit 6c4009
Packit Service 3b0880
  ent->stream = fopen ("/etc/group", "rme");
Packit 6c4009
Packit 6c4009
  if (ent->stream == NULL)
Packit 6c4009
    status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
Packit Service 3b0880
  else
Packit Service 3b0880
    /* We take care of locking ourself.  */
Packit Service 3b0880
    __fsetlocking (ent->stream, FSETLOCKING_BYCALLER);
Packit 6c4009
Packit 6c4009
  return status;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit Service 3b0880
static enum nss_status
Packit 6c4009
internal_endgrent (ent_t *ent)
Packit 6c4009
{
Packit 6c4009
  if (ent->stream != NULL)
Packit 6c4009
    {
Packit 6c4009
      fclose (ent->stream);
Packit 6c4009
      ent->stream = NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (ent->blacklist.data != NULL)
Packit 6c4009
    {
Packit 6c4009
      ent->blacklist.current = 1;
Packit 6c4009
      ent->blacklist.data[0] = '|';
Packit 6c4009
      ent->blacklist.data[1] = '\0';
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    ent->blacklist.current = 0;
Packit 6c4009
Packit 6c4009
  if (ent->need_endgrent && nss_endgrent != NULL)
Packit 6c4009
    nss_endgrent ();
Packit 6c4009
Packit 6c4009
  return NSS_STATUS_SUCCESS;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Add new group record.  */
Packit 6c4009
static void
Packit 6c4009
add_group (long int *start, long int *size, gid_t **groupsp, long int limit,
Packit 6c4009
	   gid_t gid)
Packit 6c4009
{
Packit 6c4009
  gid_t *groups = *groupsp;
Packit 6c4009
Packit 6c4009
  /* Matches user.  Insert this group.  */
Packit 6c4009
  if (__glibc_unlikely (*start == *size))
Packit 6c4009
    {
Packit 6c4009
      /* Need a bigger buffer.  */
Packit 6c4009
      gid_t *newgroups;
Packit 6c4009
      long int newsize;
Packit 6c4009
Packit 6c4009
      if (limit > 0 && *size == limit)
Packit 6c4009
	/* We reached the maximum.  */
Packit 6c4009
	return;
Packit 6c4009
Packit 6c4009
      if (limit <= 0)
Packit 6c4009
	newsize = 2 * *size;
Packit 6c4009
      else
Packit 6c4009
	newsize = MIN (limit, 2 * *size);
Packit 6c4009
Packit 6c4009
      newgroups = realloc (groups, newsize * sizeof (*groups));
Packit 6c4009
      if (newgroups == NULL)
Packit 6c4009
	return;
Packit 6c4009
      *groupsp = groups = newgroups;
Packit 6c4009
      *size = newsize;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  groups[*start] = gid;
Packit 6c4009
  *start += 1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* This function checks, if the user is a member of this group and if
Packit 6c4009
   yes, add the group id to the list.  Return nonzero is we couldn't
Packit 6c4009
   handle the group because the user is not in the member list.  */
Packit 6c4009
static int
Packit 6c4009
check_and_add_group (const char *user, gid_t group, long int *start,
Packit 6c4009
		     long int *size, gid_t **groupsp, long int limit,
Packit 6c4009
		     struct group *grp)
Packit 6c4009
{
Packit 6c4009
  char **member;
Packit 6c4009
Packit 6c4009
  /* Don't add main group to list of groups.  */
Packit 6c4009
  if (grp->gr_gid == group)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  for (member = grp->gr_mem; *member != NULL; ++member)
Packit 6c4009
    if (strcmp (*member, user) == 0)
Packit 6c4009
      {
Packit 6c4009
	add_group (start, size, groupsp, limit, grp->gr_gid);
Packit 6c4009
	return 0;
Packit 6c4009
      }
Packit 6c4009
Packit 6c4009
  return 1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Get the next group from NSS  (+ entry). If the NSS module supports
Packit 6c4009
   initgroups_dyn, get all entries at once.  */
Packit 6c4009
static enum nss_status
Packit 6c4009
getgrent_next_nss (ent_t *ent, char *buffer, size_t buflen, const char *user,
Packit 6c4009
		   gid_t group, long int *start, long int *size,
Packit 6c4009
		   gid_t **groupsp, long int limit, int *errnop)
Packit 6c4009
{
Packit 6c4009
  enum nss_status status;
Packit 6c4009
  struct group grpbuf;
Packit 6c4009
Packit 6c4009
  /* Try nss_initgroups_dyn if supported. We also need getgrgid_r.
Packit 6c4009
     If this function is not supported, step through the whole group
Packit 6c4009
     database with getgrent_r.  */
Packit 6c4009
  if (! ent->skip_initgroups_dyn)
Packit 6c4009
    {
Packit 6c4009
      long int mystart = 0;
Packit 6c4009
      long int mysize = limit <= 0 ? *size : limit;
Packit 6c4009
      gid_t *mygroups = malloc (mysize * sizeof (gid_t));
Packit 6c4009
Packit 6c4009
      if (mygroups == NULL)
Packit 6c4009
	return NSS_STATUS_TRYAGAIN;
Packit 6c4009
Packit 6c4009
      /* For every gid in the list we get from the NSS module,
Packit 6c4009
	 get the whole group entry. We need to do this, since we
Packit 6c4009
	 need the group name to check if it is in the blacklist.
Packit 6c4009
	 In worst case, this is as twice as slow as stepping with
Packit 6c4009
	 getgrent_r through the whole group database. But for large
Packit 6c4009
	 group databases this is faster, since the user can only be
Packit 6c4009
	 in a limited number of groups.  */
Packit 6c4009
      if (nss_initgroups_dyn (user, group, &mystart, &mysize, &mygroups,
Packit 6c4009
			      limit, errnop) == NSS_STATUS_SUCCESS)
Packit 6c4009
	{
Packit 6c4009
	  status = NSS_STATUS_NOTFOUND;
Packit 6c4009
Packit 6c4009
	  /* If there is no blacklist we can trust the underlying
Packit 6c4009
	     initgroups implementation.  */
Packit 6c4009
	  if (ent->blacklist.current <= 1)
Packit 6c4009
	    for (int i = 0; i < mystart; i++)
Packit 6c4009
	      add_group (start, size, groupsp, limit, mygroups[i]);
Packit 6c4009
	  else
Packit 6c4009
	    {
Packit 6c4009
	      /* A temporary buffer. We use the normal buffer, until we find
Packit 6c4009
		 an entry, for which this buffer is to small.  In this case, we
Packit 6c4009
		 overwrite the pointer with one to a bigger buffer.  */
Packit 6c4009
	      char *tmpbuf = buffer;
Packit 6c4009
	      size_t tmplen = buflen;
Packit 6c4009
Packit 6c4009
	      for (int i = 0; i < mystart; i++)
Packit 6c4009
		{
Packit 6c4009
		  while ((status = nss_getgrgid_r (mygroups[i], &grpbuf,
Packit 6c4009
						   tmpbuf, tmplen, errnop))
Packit 6c4009
			 == NSS_STATUS_TRYAGAIN
Packit 6c4009
			 && *errnop == ERANGE)
Packit 6c4009
                    {
Packit 6c4009
		      /* Check for overflow. */
Packit 6c4009
		      if (__glibc_unlikely (tmplen * 2 < tmplen))
Packit 6c4009
			{
Packit 6c4009
			  __set_errno (ENOMEM);
Packit 6c4009
			  status = NSS_STATUS_TRYAGAIN;
Packit 6c4009
			  goto done;
Packit 6c4009
			}
Packit 6c4009
		      /* Increase the size.  Make sure that we retry
Packit 6c4009
			 with a reasonable size.  */
Packit 6c4009
		      tmplen *= 2;
Packit 6c4009
		      if (tmplen < 1024)
Packit 6c4009
			tmplen = 1024;
Packit 6c4009
		      if (tmpbuf != buffer)
Packit 6c4009
			free (tmpbuf);
Packit 6c4009
		      tmpbuf = malloc (tmplen);
Packit 6c4009
		      if (__glibc_unlikely (tmpbuf == NULL))
Packit 6c4009
			{
Packit 6c4009
			  status = NSS_STATUS_TRYAGAIN;
Packit 6c4009
			  goto done;
Packit 6c4009
			}
Packit 6c4009
                    }
Packit 6c4009
Packit 6c4009
		  if (__builtin_expect  (status != NSS_STATUS_NOTFOUND, 1))
Packit 6c4009
		    {
Packit 6c4009
		      if (__builtin_expect  (status != NSS_STATUS_SUCCESS, 0))
Packit 6c4009
		        goto done;
Packit 6c4009
Packit 6c4009
		      if (!in_blacklist (grpbuf.gr_name,
Packit 6c4009
					 strlen (grpbuf.gr_name), ent)
Packit 6c4009
			  && check_and_add_group (user, group, start, size,
Packit 6c4009
						  groupsp, limit, &grpbuf))
Packit 6c4009
			{
Packit 6c4009
			  if (nss_setgrent != NULL)
Packit 6c4009
			    {
Packit 6c4009
			      nss_setgrent (1);
Packit 6c4009
			      ent->need_endgrent = true;
Packit 6c4009
			    }
Packit 6c4009
			  ent->skip_initgroups_dyn = true;
Packit 6c4009
Packit 6c4009
			  goto iter;
Packit 6c4009
			}
Packit 6c4009
		    }
Packit 6c4009
		}
Packit 6c4009
Packit 6c4009
	      status = NSS_STATUS_NOTFOUND;
Packit 6c4009
Packit 6c4009
 done:
Packit 6c4009
	      if (tmpbuf != buffer)
Packit 6c4009
	        free (tmpbuf);
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  free (mygroups);
Packit 6c4009
Packit 6c4009
	  return status;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      free (mygroups);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  /* If we come here, the NSS module does not support initgroups_dyn
Packit 6c4009
     or we were confronted with a split group.  In these cases we have
Packit 6c4009
     to step through the whole list ourself.  */
Packit 6c4009
 iter:
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      if ((status = nss_getgrent_r (&grpbuf, buffer, buflen, errnop)) !=
Packit 6c4009
	  NSS_STATUS_SUCCESS)
Packit 6c4009
	break;
Packit 6c4009
    }
Packit 6c4009
  while (in_blacklist (grpbuf.gr_name, strlen (grpbuf.gr_name), ent));
Packit 6c4009
Packit 6c4009
  if (status == NSS_STATUS_SUCCESS)
Packit 6c4009
    check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf);
Packit 6c4009
Packit 6c4009
  return status;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static enum nss_status
Packit 6c4009
internal_getgrent_r (ent_t *ent, char *buffer, size_t buflen, const char *user,
Packit 6c4009
		     gid_t group, long int *start, long int *size,
Packit 6c4009
		     gid_t **groupsp, long int limit, int *errnop)
Packit 6c4009
{
Packit 6c4009
  struct parser_data *data = (void *) buffer;
Packit 6c4009
  struct group grpbuf;
Packit 6c4009
Packit 6c4009
  if (!ent->files)
Packit 6c4009
    return getgrent_next_nss (ent, buffer, buflen, user, group,
Packit 6c4009
			      start, size, groupsp, limit, errnop);
Packit 6c4009
Packit 6c4009
  while (1)
Packit 6c4009
    {
Packit 6c4009
      fpos_t pos;
Packit 6c4009
      int parse_res = 0;
Packit 6c4009
      char *p;
Packit 6c4009
Packit 6c4009
      do
Packit 6c4009
	{
Packit 6c4009
	  /* We need at least 3 characters for one line.  */
Packit 6c4009
	  if (__glibc_unlikely (buflen < 3))
Packit 6c4009
	    {
Packit 6c4009
	    erange:
Packit 6c4009
	      *errnop = ERANGE;
Packit 6c4009
	      return NSS_STATUS_TRYAGAIN;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  fgetpos (ent->stream, &pos;;
Packit 6c4009
	  buffer[buflen - 1] = '\xff';
Packit 6c4009
	  p = fgets_unlocked (buffer, buflen, ent->stream);
Packit 6c4009
	  if (p == NULL && feof_unlocked (ent->stream))
Packit 6c4009
	    return NSS_STATUS_NOTFOUND;
Packit 6c4009
Packit 6c4009
	  if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
Packit 6c4009
	    {
Packit 6c4009
	    erange_reset:
Packit 6c4009
	      fsetpos (ent->stream, &pos;;
Packit 6c4009
	      goto erange;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  /* Terminate the line for any case.  */
Packit 6c4009
	  buffer[buflen - 1] = '\0';
Packit 6c4009
Packit 6c4009
	  /* Skip leading blanks.  */
Packit 6c4009
	  while (isspace (*p))
Packit 6c4009
	    ++p;
Packit 6c4009
	}
Packit 6c4009
      while (*p == '\0' || *p == '#' ||	/* Ignore empty and comment lines. */
Packit 6c4009
	     /* Parse the line.  If it is invalid, loop to
Packit 6c4009
		get the next line of the file to parse.  */
Packit 6c4009
	     !(parse_res = _nss_files_parse_grent (p, &grpbuf, data, buflen,
Packit 6c4009
						   errnop)));
Packit 6c4009
Packit 6c4009
      if (__glibc_unlikely (parse_res == -1))
Packit 6c4009
	/* The parser ran out of space.  */
Packit 6c4009
	goto erange_reset;
Packit 6c4009
Packit 6c4009
      if (grpbuf.gr_name[0] != '+' && grpbuf.gr_name[0] != '-')
Packit 6c4009
	/* This is a real entry.  */
Packit 6c4009
	break;
Packit 6c4009
Packit 6c4009
      /* -group */
Packit 6c4009
      if (grpbuf.gr_name[0] == '-' && grpbuf.gr_name[1] != '\0'
Packit 6c4009
	  && grpbuf.gr_name[1] != '@')
Packit 6c4009
	{
Packit 6c4009
	  blacklist_store_name (&grpbuf.gr_name[1], ent);
Packit 6c4009
	  continue;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* +group */
Packit 6c4009
      if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] != '\0'
Packit 6c4009
	  && grpbuf.gr_name[1] != '@')
Packit 6c4009
	{
Packit 6c4009
	  if (in_blacklist (&grpbuf.gr_name[1],
Packit 6c4009
			    strlen (&grpbuf.gr_name[1]), ent))
Packit 6c4009
	    continue;
Packit 6c4009
	  /* Store the group in the blacklist for the "+" at the end of
Packit 6c4009
	     /etc/group */
Packit 6c4009
	  blacklist_store_name (&grpbuf.gr_name[1], ent);
Packit 6c4009
	  if (nss_getgrnam_r == NULL)
Packit 6c4009
	    return NSS_STATUS_UNAVAIL;
Packit 6c4009
	  else if (nss_getgrnam_r (&grpbuf.gr_name[1], &grpbuf, buffer,
Packit 6c4009
				   buflen, errnop) != NSS_STATUS_SUCCESS)
Packit 6c4009
	    continue;
Packit 6c4009
Packit 6c4009
	  check_and_add_group (user, group, start, size, groupsp,
Packit 6c4009
			       limit, &grpbuf);
Packit 6c4009
Packit 6c4009
	  return NSS_STATUS_SUCCESS;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      /* +:... */
Packit 6c4009
      if (grpbuf.gr_name[0] == '+' && grpbuf.gr_name[1] == '\0')
Packit 6c4009
	{
Packit 6c4009
	  /* If the selected module does not support getgrent_r or
Packit 6c4009
	     initgroups_dyn, abort. We cannot find the needed group
Packit 6c4009
	     entries.  */
Packit 6c4009
	  if (nss_initgroups_dyn == NULL || nss_getgrgid_r == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      if (nss_setgrent != NULL)
Packit 6c4009
		{
Packit 6c4009
		  nss_setgrent (1);
Packit 6c4009
		  ent->need_endgrent = true;
Packit 6c4009
		}
Packit 6c4009
	      ent->skip_initgroups_dyn = true;
Packit 6c4009
Packit 6c4009
	      if (nss_getgrent_r == NULL)
Packit 6c4009
		return NSS_STATUS_UNAVAIL;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  ent->files = false;
Packit 6c4009
Packit 6c4009
	  return getgrent_next_nss (ent, buffer, buflen, user, group,
Packit 6c4009
				    start, size, groupsp, limit, errnop);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  check_and_add_group (user, group, start, size, groupsp, limit, &grpbuf);
Packit 6c4009
Packit 6c4009
  return NSS_STATUS_SUCCESS;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
enum nss_status
Packit 6c4009
_nss_compat_initgroups_dyn (const char *user, gid_t group, long int *start,
Packit 6c4009
			    long int *size, gid_t **groupsp, long int limit,
Packit 6c4009
			    int *errnop)
Packit 6c4009
{
Packit 6c4009
  enum nss_status status;
Packit 6c4009
  ent_t intern = { true, false, false, NULL, {NULL, 0, 0} };
Packit 6c4009
Packit 6c4009
  status = internal_setgrent (&intern;;
Packit 6c4009
  if (status != NSS_STATUS_SUCCESS)
Packit 6c4009
    return status;
Packit 6c4009
Packit 6c4009
  struct scratch_buffer tmpbuf;
Packit 6c4009
  scratch_buffer_init (&tmpbuf);
Packit 6c4009
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      while ((status = internal_getgrent_r (&intern, tmpbuf.data, tmpbuf.length,
Packit 6c4009
					    user, group, start, size,
Packit 6c4009
					    groupsp, limit, errnop))
Packit 6c4009
	     == NSS_STATUS_TRYAGAIN && *errnop == ERANGE)
Packit 6c4009
        if (!scratch_buffer_grow (&tmpbuf))
Packit 6c4009
	    goto done;
Packit 6c4009
    }
Packit 6c4009
  while (status == NSS_STATUS_SUCCESS);
Packit 6c4009
Packit 6c4009
  status = NSS_STATUS_SUCCESS;
Packit 6c4009
Packit 6c4009
 done:
Packit 6c4009
  scratch_buffer_free (&tmpbuf);
Packit 6c4009
Packit Service 3b0880
  internal_endgrent (&intern;;
Packit 6c4009
Packit 6c4009
  return status;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
/* Support routines for remembering -@netgroup and -user entries.
Packit 6c4009
   The names are stored in a single string with `|' as separator. */
Packit 6c4009
static void
Packit 6c4009
blacklist_store_name (const char *name, ent_t *ent)
Packit 6c4009
{
Packit 6c4009
  int namelen = strlen (name);
Packit 6c4009
  char *tmp;
Packit 6c4009
Packit 6c4009
  /* First call, setup cache.  */
Packit 6c4009
  if (ent->blacklist.size == 0)
Packit 6c4009
    {
Packit 6c4009
      ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
Packit 6c4009
      ent->blacklist.data = malloc (ent->blacklist.size);
Packit 6c4009
      if (ent->blacklist.data == NULL)
Packit 6c4009
	return;
Packit 6c4009
      ent->blacklist.data[0] = '|';
Packit 6c4009
      ent->blacklist.data[1] = '\0';
Packit 6c4009
      ent->blacklist.current = 1;
Packit 6c4009
    }
Packit 6c4009
  else
Packit 6c4009
    {
Packit 6c4009
      if (in_blacklist (name, namelen, ent))
Packit 6c4009
	return;			/* no duplicates */
Packit 6c4009
Packit 6c4009
      if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
Packit 6c4009
	{
Packit 6c4009
	  ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
Packit 6c4009
	  tmp = realloc (ent->blacklist.data, ent->blacklist.size);
Packit 6c4009
	  if (tmp == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      free (ent->blacklist.data);
Packit 6c4009
	      ent->blacklist.size = 0;
Packit 6c4009
	      return;
Packit 6c4009
	    }
Packit 6c4009
	  ent->blacklist.data = tmp;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
Packit 6c4009
  *tmp++ = '|';
Packit 6c4009
  *tmp = '\0';
Packit 6c4009
  ent->blacklist.current += namelen + 1;
Packit 6c4009
Packit 6c4009
  return;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
/* Return whether ent->blacklist contains name.  */
Packit 6c4009
static bool
Packit 6c4009
in_blacklist (const char *name, int namelen, ent_t *ent)
Packit 6c4009
{
Packit 6c4009
  char buf[namelen + 3];
Packit 6c4009
  char *cp;
Packit 6c4009
Packit 6c4009
  if (ent->blacklist.data == NULL)
Packit 6c4009
    return false;
Packit 6c4009
Packit 6c4009
  buf[0] = '|';
Packit 6c4009
  cp = stpcpy (&buf[1], name);
Packit 6c4009
  *cp++ = '|';
Packit 6c4009
  *cp = '\0';
Packit 6c4009
  return strstr (ent->blacklist.data, buf) != NULL;
Packit 6c4009
}