(root)/
glibc-2.38/
nss/
nss_compat/
compat-pwd.c
       1  /* Copyright (C) 1996-2023 Free Software Foundation, Inc.
       2     This file is part of the GNU C Library.
       3  
       4     The GNU C Library is free software; you can redistribute it and/or
       5     modify it under the terms of the GNU Lesser General Public
       6     License as published by the Free Software Foundation; either
       7     version 2.1 of the License, or (at your option) any later version.
       8  
       9     The GNU C Library is distributed in the hope that it will be useful,
      10     but WITHOUT ANY WARRANTY; without even the implied warranty of
      11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12     Lesser General Public License for more details.
      13  
      14     You should have received a copy of the GNU Lesser General Public
      15     License along with the GNU C Library; if not, see
      16     <https://www.gnu.org/licenses/>.  */
      17  
      18  #include <ctype.h>
      19  #include <errno.h>
      20  #include <fcntl.h>
      21  #include <netdb.h>
      22  #include <nss.h>
      23  #include <nsswitch.h>
      24  #include <pwd.h>
      25  #include <stdio_ext.h>
      26  #include <string.h>
      27  #include <libc-lock.h>
      28  #include <kernel-features.h>
      29  #include <nss_files.h>
      30  
      31  #include "netgroup.h"
      32  #include "nisdomain.h"
      33  
      34  NSS_DECLARE_MODULE_FUNCTIONS (compat)
      35  
      36  static nss_action_list ni;
      37  static enum nss_status (*setpwent_impl) (int stayopen);
      38  static enum nss_status (*getpwnam_r_impl) (const char *name,
      39  					   struct passwd * pwd, char *buffer,
      40  					   size_t buflen, int *errnop);
      41  static enum nss_status (*getpwuid_r_impl) (uid_t uid, struct passwd * pwd,
      42  					   char *buffer, size_t buflen,
      43  					   int *errnop);
      44  static enum nss_status (*getpwent_r_impl) (struct passwd * pwd, char *buffer,
      45  					   size_t buflen, int *errnop);
      46  static enum nss_status (*endpwent_impl) (void);
      47  
      48  /* Get the declaration of the parser function.  */
      49  #define ENTNAME pwent
      50  #define STRUCTURE passwd
      51  #define EXTERN_PARSER
      52  #include <nss/nss_files/files-parse.c>
      53  
      54  /* Structure for remembering -@netgroup and -user members ... */
      55  #define BLACKLIST_INITIAL_SIZE 512
      56  #define BLACKLIST_INCREMENT 256
      57  struct blacklist_t
      58  {
      59    char *data;
      60    int current;
      61    int size;
      62  };
      63  
      64  struct ent_t
      65  {
      66    bool netgroup;
      67    bool first;
      68    bool files;
      69    enum nss_status setent_status;
      70    FILE *stream;
      71    struct blacklist_t blacklist;
      72    struct passwd pwd;
      73    struct __netgrent netgrdata;
      74  };
      75  typedef struct ent_t ent_t;
      76  
      77  static ent_t ext_ent = { false, false, true, NSS_STATUS_SUCCESS, NULL,
      78  			 { NULL, 0, 0 },
      79  			 { NULL, NULL, 0, 0, NULL, NULL, NULL }};
      80  
      81  /* Protect global state against multiple changers.  */
      82  __libc_lock_define_initialized (static, lock)
      83  
      84  /* Prototypes for local functions.  */
      85  static void blacklist_store_name (const char *, ent_t *);
      86  static bool in_blacklist (const char *, int, ent_t *);
      87  
      88  /* Initialize the NSS interface/functions. The calling function must
      89     hold the lock.  */
      90  static void
      91  init_nss_interface (void)
      92  {
      93    if (__nss_database_get (nss_database_passwd_compat, &ni))
      94      {
      95        setpwent_impl = __nss_lookup_function (ni, "setpwent");
      96        getpwnam_r_impl = __nss_lookup_function (ni, "getpwnam_r");
      97        getpwuid_r_impl = __nss_lookup_function (ni, "getpwuid_r");
      98        getpwent_r_impl = __nss_lookup_function (ni, "getpwent_r");
      99        endpwent_impl = __nss_lookup_function (ni, "endpwent");
     100      }
     101  }
     102  
     103  static void
     104  give_pwd_free (struct passwd *pwd)
     105  {
     106    free (pwd->pw_name);
     107    free (pwd->pw_passwd);
     108    free (pwd->pw_gecos);
     109    free (pwd->pw_dir);
     110    free (pwd->pw_shell);
     111  
     112    memset (pwd, '\0', sizeof (struct passwd));
     113  }
     114  
     115  static size_t
     116  pwd_need_buflen (struct passwd *pwd)
     117  {
     118    size_t len = 0;
     119  
     120    if (pwd->pw_passwd != NULL)
     121      len += strlen (pwd->pw_passwd) + 1;
     122  
     123    if (pwd->pw_gecos != NULL)
     124      len += strlen (pwd->pw_gecos) + 1;
     125  
     126    if (pwd->pw_dir != NULL)
     127      len += strlen (pwd->pw_dir) + 1;
     128  
     129    if (pwd->pw_shell != NULL)
     130      len += strlen (pwd->pw_shell) + 1;
     131  
     132    return len;
     133  }
     134  
     135  static void
     136  copy_pwd_changes (struct passwd *dest, struct passwd *src,
     137  		  char *buffer, size_t buflen)
     138  {
     139    if (src->pw_passwd != NULL && strlen (src->pw_passwd))
     140      {
     141        if (buffer == NULL)
     142  	dest->pw_passwd = strdup (src->pw_passwd);
     143        else if (dest->pw_passwd
     144  	       && strlen (dest->pw_passwd) >= strlen (src->pw_passwd))
     145  	strcpy (dest->pw_passwd, src->pw_passwd);
     146        else
     147  	{
     148  	  dest->pw_passwd = buffer;
     149  	  strcpy (dest->pw_passwd, src->pw_passwd);
     150  	  buffer += strlen (dest->pw_passwd) + 1;
     151  	  buflen = buflen - (strlen (dest->pw_passwd) + 1);
     152  	}
     153      }
     154  
     155    if (src->pw_gecos != NULL && strlen (src->pw_gecos))
     156      {
     157        if (buffer == NULL)
     158  	dest->pw_gecos = strdup (src->pw_gecos);
     159        else if (dest->pw_gecos
     160  	       && strlen (dest->pw_gecos) >= strlen (src->pw_gecos))
     161  	strcpy (dest->pw_gecos, src->pw_gecos);
     162        else
     163  	{
     164  	  dest->pw_gecos = buffer;
     165  	  strcpy (dest->pw_gecos, src->pw_gecos);
     166  	  buffer += strlen (dest->pw_gecos) + 1;
     167  	  buflen = buflen - (strlen (dest->pw_gecos) + 1);
     168  	}
     169      }
     170    if (src->pw_dir != NULL && strlen (src->pw_dir))
     171      {
     172        if (buffer == NULL)
     173  	dest->pw_dir = strdup (src->pw_dir);
     174        else if (dest->pw_dir && strlen (dest->pw_dir) >= strlen (src->pw_dir))
     175  	strcpy (dest->pw_dir, src->pw_dir);
     176        else
     177  	{
     178  	  dest->pw_dir = buffer;
     179  	  strcpy (dest->pw_dir, src->pw_dir);
     180  	  buffer += strlen (dest->pw_dir) + 1;
     181  	  buflen = buflen - (strlen (dest->pw_dir) + 1);
     182  	}
     183      }
     184  
     185    if (src->pw_shell != NULL && strlen (src->pw_shell))
     186      {
     187        if (buffer == NULL)
     188  	dest->pw_shell = strdup (src->pw_shell);
     189        else if (dest->pw_shell
     190  	       && strlen (dest->pw_shell) >= strlen (src->pw_shell))
     191  	strcpy (dest->pw_shell, src->pw_shell);
     192        else
     193  	{
     194  	  dest->pw_shell = buffer;
     195  	  strcpy (dest->pw_shell, src->pw_shell);
     196  	  buffer += strlen (dest->pw_shell) + 1;
     197  	  buflen = buflen - (strlen (dest->pw_shell) + 1);
     198  	}
     199      }
     200  }
     201  
     202  static enum nss_status
     203  internal_setpwent (ent_t *ent, int stayopen, int needent)
     204  {
     205    enum nss_status status = NSS_STATUS_SUCCESS;
     206  
     207    ent->first = ent->netgroup = false;
     208    ent->files = true;
     209    ent->setent_status = NSS_STATUS_SUCCESS;
     210  
     211    /* If something was left over free it.  */
     212    if (ent->netgroup)
     213      __internal_endnetgrent (&ent->netgrdata);
     214  
     215    if (ent->blacklist.data != NULL)
     216      {
     217        ent->blacklist.current = 1;
     218        ent->blacklist.data[0] = '|';
     219        ent->blacklist.data[1] = '\0';
     220      }
     221    else
     222      ent->blacklist.current = 0;
     223  
     224    if (ent->stream == NULL)
     225      {
     226        ent->stream = __nss_files_fopen ("/etc/passwd");
     227  
     228        if (ent->stream == NULL)
     229  	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
     230      }
     231    else
     232      rewind (ent->stream);
     233  
     234    give_pwd_free (&ent->pwd);
     235  
     236    if (needent && status == NSS_STATUS_SUCCESS && setpwent_impl)
     237      ent->setent_status = setpwent_impl (stayopen);
     238  
     239    return status;
     240  }
     241  
     242  
     243  enum nss_status
     244  _nss_compat_setpwent (int stayopen)
     245  {
     246    enum nss_status result;
     247  
     248    __libc_lock_lock (lock);
     249  
     250    if (ni == NULL)
     251      init_nss_interface ();
     252  
     253    result = internal_setpwent (&ext_ent, stayopen, 1);
     254  
     255    __libc_lock_unlock (lock);
     256  
     257    return result;
     258  }
     259  
     260  
     261  static enum nss_status __attribute_warn_unused_result__
     262  internal_endpwent (ent_t *ent)
     263  {
     264    if (ent->stream != NULL)
     265      {
     266        fclose (ent->stream);
     267        ent->stream = NULL;
     268      }
     269  
     270    if (ent->netgroup)
     271      __internal_endnetgrent (&ent->netgrdata);
     272  
     273    ent->first = ent->netgroup = false;
     274  
     275    if (ent->blacklist.data != NULL)
     276      {
     277        ent->blacklist.current = 1;
     278        ent->blacklist.data[0] = '|';
     279        ent->blacklist.data[1] = '\0';
     280      }
     281    else
     282      ent->blacklist.current = 0;
     283  
     284    give_pwd_free (&ent->pwd);
     285  
     286    return NSS_STATUS_SUCCESS;
     287  }
     288  
     289  /* Like internal_endpwent, but preserve errno in all cases.  */
     290  static void
     291  internal_endpwent_noerror (ent_t *ent)
     292  {
     293    int saved_errno = errno;
     294    enum nss_status unused __attribute__ ((unused)) = internal_endpwent (ent);
     295    __set_errno (saved_errno);
     296  }
     297  
     298  enum nss_status
     299  _nss_compat_endpwent (void)
     300  {
     301    enum nss_status result;
     302  
     303    __libc_lock_lock (lock);
     304  
     305    if (endpwent_impl)
     306      endpwent_impl ();
     307  
     308    result = internal_endpwent (&ext_ent);
     309  
     310    __libc_lock_unlock (lock);
     311  
     312    return result;
     313  }
     314  
     315  
     316  static enum nss_status
     317  getpwent_next_nss_netgr (const char *name, struct passwd *result, ent_t *ent,
     318  			 char *group, char *buffer, size_t buflen,
     319  			 int *errnop)
     320  {
     321    char *curdomain = NULL, *host, *user, *domain, *p2;
     322    int status;
     323    size_t p2len;
     324  
     325    /* Leave function if NSS module does not support getpwnam_r,
     326       we need this function here.  */
     327    if (!getpwnam_r_impl)
     328      return NSS_STATUS_UNAVAIL;
     329  
     330    if (ent->first)
     331      {
     332        memset (&ent->netgrdata, 0, sizeof (struct __netgrent));
     333        __internal_setnetgrent (group, &ent->netgrdata);
     334        ent->first = false;
     335      }
     336  
     337    while (1)
     338      {
     339        status = __internal_getnetgrent_r (&host, &user, &domain,
     340  					 &ent->netgrdata, buffer, buflen,
     341  					 errnop);
     342        if (status != 1)
     343  	{
     344  	  __internal_endnetgrent (&ent->netgrdata);
     345  	  ent->netgroup = 0;
     346  	  give_pwd_free (&ent->pwd);
     347  	  return NSS_STATUS_RETURN;
     348  	}
     349  
     350        if (user == NULL || user[0] == '-')
     351  	continue;
     352  
     353        if (domain != NULL)
     354  	{
     355  	  if (curdomain == NULL
     356  	      && __nss_get_default_domain (&curdomain) != 0)
     357  	    {
     358  	      __internal_endnetgrent (&ent->netgrdata);
     359  	      ent->netgroup = false;
     360  	      give_pwd_free (&ent->pwd);
     361  	      return NSS_STATUS_UNAVAIL;
     362  	    }
     363  	  if (strcmp (curdomain, domain) != 0)
     364  	    continue;
     365  	}
     366  
     367        /* If name != NULL, we are called from getpwnam.  */
     368        if (name != NULL)
     369  	if (strcmp (user, name) != 0)
     370  	  continue;
     371  
     372        p2len = pwd_need_buflen (&ent->pwd);
     373        if (p2len > buflen)
     374  	{
     375  	  *errnop = ERANGE;
     376  	  return NSS_STATUS_TRYAGAIN;
     377  	}
     378        p2 = buffer + (buflen - p2len);
     379        buflen -= p2len;
     380  
     381        if (getpwnam_r_impl (user, result, buffer, buflen, errnop)
     382  	  != NSS_STATUS_SUCCESS)
     383  	continue;
     384  
     385        if (!in_blacklist (result->pw_name, strlen (result->pw_name), ent))
     386  	{
     387  	  /* Store the User in the blacklist for possible the "+" at the
     388  	     end of /etc/passwd */
     389  	  blacklist_store_name (result->pw_name, ent);
     390  	  copy_pwd_changes (result, &ent->pwd, p2, p2len);
     391  	  break;
     392  	}
     393      }
     394  
     395    return NSS_STATUS_SUCCESS;
     396  }
     397  
     398  /* get the next user from NSS  (+ entry) */
     399  static enum nss_status
     400  getpwent_next_nss (struct passwd *result, ent_t *ent, char *buffer,
     401  		   size_t buflen, int *errnop)
     402  {
     403    enum nss_status status;
     404    char *p2;
     405    size_t p2len;
     406  
     407    /* Return if NSS module does not support getpwent_r.  */
     408    if (!getpwent_r_impl)
     409      return NSS_STATUS_UNAVAIL;
     410  
     411    /* If the setpwent call failed, say so.  */
     412    if (ent->setent_status != NSS_STATUS_SUCCESS)
     413      return ent->setent_status;
     414  
     415    p2len = pwd_need_buflen (&ent->pwd);
     416    if (p2len > buflen)
     417      {
     418        *errnop = ERANGE;
     419        return NSS_STATUS_TRYAGAIN;
     420      }
     421    p2 = buffer + (buflen - p2len);
     422    buflen -= p2len;
     423  
     424    if (ent->first)
     425      ent->first = false;
     426  
     427    do
     428      {
     429        if ((status = getpwent_r_impl (result, buffer, buflen, errnop))
     430  	  != NSS_STATUS_SUCCESS)
     431  	return status;
     432      }
     433    while (in_blacklist (result->pw_name, strlen (result->pw_name), ent));
     434  
     435    copy_pwd_changes (result, &ent->pwd, p2, p2len);
     436  
     437    return NSS_STATUS_SUCCESS;
     438  }
     439  
     440  /* This function handle the +user entries in /etc/passwd */
     441  static enum nss_status
     442  getpwnam_plususer (const char *name, struct passwd *result, ent_t *ent,
     443  		   char *buffer, size_t buflen, int *errnop)
     444  {
     445    if (!getpwnam_r_impl)
     446      return NSS_STATUS_UNAVAIL;
     447  
     448    struct passwd pwd;
     449    memset (&pwd, '\0', sizeof (struct passwd));
     450  
     451    copy_pwd_changes (&pwd, result, NULL, 0);
     452  
     453    size_t plen = pwd_need_buflen (&pwd);
     454    if (plen > buflen)
     455      {
     456        *errnop = ERANGE;
     457        return NSS_STATUS_TRYAGAIN;
     458      }
     459    char *p = buffer + (buflen - plen);
     460    buflen -= plen;
     461  
     462    enum nss_status status = getpwnam_r_impl (name, result, buffer, buflen,
     463  					    errnop);
     464    if (status != NSS_STATUS_SUCCESS)
     465      return status;
     466  
     467    if (in_blacklist (result->pw_name, strlen (result->pw_name), ent))
     468      return NSS_STATUS_NOTFOUND;
     469  
     470    copy_pwd_changes (result, &pwd, p, plen);
     471    give_pwd_free (&pwd);
     472    /* We found the entry.  */
     473    return NSS_STATUS_SUCCESS;
     474  }
     475  
     476  static enum nss_status
     477  getpwent_next_file (struct passwd *result, ent_t *ent,
     478  		    char *buffer, size_t buflen, int *errnop)
     479  {
     480    struct parser_data *data = (void *) buffer;
     481    while (1)
     482      {
     483        fpos_t pos;
     484        char *p;
     485        int parse_res;
     486  
     487        do
     488  	{
     489  	  /* We need at least 3 characters for one line.  */
     490  	  if (__glibc_unlikely (buflen < 3))
     491  	    {
     492  	    erange:
     493  	      *errnop = ERANGE;
     494  	      return NSS_STATUS_TRYAGAIN;
     495  	    }
     496  
     497  	  fgetpos (ent->stream, &pos);
     498  	  buffer[buflen - 1] = '\xff';
     499  	  p = fgets_unlocked (buffer, buflen, ent->stream);
     500  	  if (p == NULL && feof_unlocked (ent->stream))
     501  	    return NSS_STATUS_NOTFOUND;
     502  
     503  	  if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
     504  	    {
     505  	    erange_reset:
     506  	      fsetpos (ent->stream, &pos);
     507  	      goto erange;
     508  	    }
     509  
     510  	  /* Terminate the line for any case.  */
     511  	  buffer[buflen - 1] = '\0';
     512  
     513  	  /* Skip leading blanks.  */
     514  	  while (isspace (*p))
     515  	    ++p;
     516  	}
     517        while (*p == '\0' || *p == '#' /* Ignore empty and comment lines.  */
     518  	     /* Parse the line.  If it is invalid, loop to
     519  	        get the next line of the file to parse.  */
     520  	     || !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
     521  						      errnop)));
     522  
     523        if (__glibc_unlikely (parse_res == -1))
     524  	/* The parser ran out of space.  */
     525  	goto erange_reset;
     526  
     527        if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
     528  	/* This is a real entry.  */
     529  	break;
     530  
     531        /* -@netgroup */
     532        if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
     533  	  && result->pw_name[2] != '\0')
     534  	{
     535  	  /* XXX Do not use fixed length buffer.  */
     536  	  char buf2[1024];
     537  	  char *user, *host, *domain;
     538  	  struct __netgrent netgrdata;
     539  
     540  	  memset (&netgrdata, 0, sizeof (struct __netgrent));
     541  	  __internal_setnetgrent (&result->pw_name[2], &netgrdata);
     542  	  while (__internal_getnetgrent_r (&host, &user, &domain, &netgrdata,
     543  					   buf2, sizeof (buf2), errnop))
     544  	    {
     545  	      if (user != NULL && user[0] != '-')
     546  		blacklist_store_name (user, ent);
     547  	    }
     548  	  __internal_endnetgrent (&netgrdata);
     549  	  continue;
     550  	}
     551  
     552        /* +@netgroup */
     553        if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
     554  	  && result->pw_name[2] != '\0')
     555  	{
     556  	  enum nss_status status;
     557  
     558  	  ent->netgroup = true;
     559  	  ent->first = true;
     560  	  copy_pwd_changes (&ent->pwd, result, NULL, 0);
     561  
     562  	  status = getpwent_next_nss_netgr (NULL, result, ent,
     563  					    &result->pw_name[2],
     564  					    buffer, buflen, errnop);
     565  	  if (status == NSS_STATUS_RETURN)
     566  	    continue;
     567  	  else
     568  	    return status;
     569  	}
     570  
     571        /* -user */
     572        if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
     573  	  && result->pw_name[1] != '@')
     574  	{
     575  	  blacklist_store_name (&result->pw_name[1], ent);
     576  	  continue;
     577  	}
     578  
     579        /* +user */
     580        if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
     581  	  && result->pw_name[1] != '@')
     582  	{
     583  	  size_t len = strlen (result->pw_name);
     584  	  char buf[len];
     585  	  enum nss_status status;
     586  
     587  	  /* Store the User in the blacklist for the "+" at the end of
     588  	     /etc/passwd */
     589  	  memcpy (buf, &result->pw_name[1], len);
     590  	  status = getpwnam_plususer (&result->pw_name[1], result, ent,
     591  				      buffer, buflen, errnop);
     592  	  blacklist_store_name (buf, ent);
     593  
     594  	  if (status == NSS_STATUS_SUCCESS)	/* We found the entry. */
     595  	    break;
     596  	  else if (status == NSS_STATUS_RETURN	/* We couldn't parse the entry */
     597  		   || status == NSS_STATUS_NOTFOUND)	/* entry doesn't exist */
     598  	    continue;
     599  	  else
     600  	    {
     601  	      if (status == NSS_STATUS_TRYAGAIN)
     602  		{
     603  		  /* The parser ran out of space */
     604  		  fsetpos (ent->stream, &pos);
     605  		  *errnop = ERANGE;
     606  		}
     607  	      return status;
     608  	    }
     609  	}
     610  
     611        /* +:... */
     612        if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
     613  	{
     614  	  ent->files = false;
     615  	  ent->first = true;
     616  	  copy_pwd_changes (&ent->pwd, result, NULL, 0);
     617  
     618  	  return getpwent_next_nss (result, ent, buffer, buflen, errnop);
     619  	}
     620      }
     621  
     622    return NSS_STATUS_SUCCESS;
     623  }
     624  
     625  
     626  static enum nss_status
     627  internal_getpwent_r (struct passwd *pw, ent_t *ent, char *buffer,
     628  		     size_t buflen, int *errnop)
     629  {
     630    if (ent->netgroup)
     631      {
     632        enum nss_status status;
     633  
     634        /* We are searching members in a netgroup */
     635        /* Since this is not the first call, we don't need the group name */
     636        status = getpwent_next_nss_netgr (NULL, pw, ent, NULL, buffer, buflen,
     637  					errnop);
     638        if (status == NSS_STATUS_RETURN)
     639  	return getpwent_next_file (pw, ent, buffer, buflen, errnop);
     640        else
     641  	return status;
     642      }
     643    else if (ent->files)
     644      return getpwent_next_file (pw, ent, buffer, buflen, errnop);
     645    else
     646      return getpwent_next_nss (pw, ent, buffer, buflen, errnop);
     647  
     648  }
     649  
     650  enum nss_status
     651  _nss_compat_getpwent_r (struct passwd *pwd, char *buffer, size_t buflen,
     652  			int *errnop)
     653  {
     654    enum nss_status result = NSS_STATUS_SUCCESS;
     655  
     656    __libc_lock_lock (lock);
     657  
     658    /* Be prepared that the setpwent function was not called before.  */
     659    if (ni == NULL)
     660      init_nss_interface ();
     661  
     662    if (ext_ent.stream == NULL)
     663      result = internal_setpwent (&ext_ent, 1, 1);
     664  
     665    if (result == NSS_STATUS_SUCCESS)
     666      result = internal_getpwent_r (pwd, &ext_ent, buffer, buflen, errnop);
     667  
     668    __libc_lock_unlock (lock);
     669  
     670    return result;
     671  }
     672  
     673  /* Searches in /etc/passwd and the NIS/NIS+ map for a special user */
     674  static enum nss_status
     675  internal_getpwnam_r (const char *name, struct passwd *result, ent_t *ent,
     676  		     char *buffer, size_t buflen, int *errnop)
     677  {
     678    struct parser_data *data = (void *) buffer;
     679  
     680    while (1)
     681      {
     682        fpos_t pos;
     683        char *p;
     684        int parse_res;
     685  
     686        do
     687  	{
     688  	  /* We need at least 3 characters for one line.  */
     689  	  if (__glibc_unlikely (buflen < 3))
     690  	    {
     691  	    erange:
     692  	      *errnop = ERANGE;
     693  	      return NSS_STATUS_TRYAGAIN;
     694  	    }
     695  
     696  	  fgetpos (ent->stream, &pos);
     697  	  buffer[buflen - 1] = '\xff';
     698  	  p = fgets_unlocked (buffer, buflen, ent->stream);
     699  	  if (p == NULL && feof_unlocked (ent->stream))
     700  	    {
     701  	      return NSS_STATUS_NOTFOUND;
     702  	    }
     703  	  if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
     704  	    {
     705  	    erange_reset:
     706  	      fsetpos (ent->stream, &pos);
     707  	      goto erange;
     708  	    }
     709  
     710  	  /* Terminate the line for any case.  */
     711  	  buffer[buflen - 1] = '\0';
     712  
     713  	  /* Skip leading blanks.  */
     714  	  while (isspace (*p))
     715  	    ++p;
     716  	}
     717        while (*p == '\0' || *p == '#' /* Ignore empty and comment lines.  */
     718  	     /* Parse the line.  If it is invalid, loop to
     719  	        get the next line of the file to parse.  */
     720  	     || !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
     721  						      errnop)));
     722  
     723        if (__glibc_unlikely (parse_res == -1))
     724  	/* The parser ran out of space.  */
     725  	goto erange_reset;
     726  
     727        /* This is a real entry.  */
     728        if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
     729  	{
     730  	  if (strcmp (result->pw_name, name) == 0)
     731  	    return NSS_STATUS_SUCCESS;
     732  	  else
     733  	    continue;
     734  	}
     735  
     736        /* -@netgroup */
     737        if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
     738  	  && result->pw_name[2] != '\0')
     739  	{
     740  	  if (innetgr (&result->pw_name[2], NULL, name, NULL))
     741  	    return NSS_STATUS_NOTFOUND;
     742  	  continue;
     743  	}
     744  
     745        /* +@netgroup */
     746        if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
     747  	  && result->pw_name[2] != '\0')
     748  	{
     749  	  enum nss_status status;
     750  
     751  	  if (innetgr (&result->pw_name[2], NULL, name, NULL))
     752  	    {
     753  	      status = getpwnam_plususer (name, result, ent, buffer,
     754  					  buflen, errnop);
     755  
     756  	      if (status == NSS_STATUS_RETURN)
     757  		continue;
     758  
     759  	      return status;
     760  	    }
     761  	  continue;
     762  	}
     763  
     764        /* -user */
     765        if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
     766  	  && result->pw_name[1] != '@')
     767  	{
     768  	  if (strcmp (&result->pw_name[1], name) == 0)
     769  	    return NSS_STATUS_NOTFOUND;
     770  	  else
     771  	    continue;
     772  	}
     773  
     774        /* +user */
     775        if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
     776  	  && result->pw_name[1] != '@')
     777  	{
     778  	  if (strcmp (name, &result->pw_name[1]) == 0)
     779  	    {
     780  	      enum nss_status status;
     781  
     782  	      status = getpwnam_plususer (name, result, ent, buffer, buflen,
     783  					  errnop);
     784  	      if (status == NSS_STATUS_RETURN)
     785  		/* We couldn't parse the entry */
     786  		return NSS_STATUS_NOTFOUND;
     787  	      else
     788  		return status;
     789  	    }
     790  	}
     791  
     792        /* +:... */
     793        if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
     794  	{
     795  	  enum nss_status status;
     796  
     797  	  status = getpwnam_plususer (name, result, ent,
     798  				      buffer, buflen, errnop);
     799  	  if (status == NSS_STATUS_SUCCESS)	/* We found the entry. */
     800  	    break;
     801  	  else if (status == NSS_STATUS_RETURN)	/* We couldn't parse the entry */
     802  	    return NSS_STATUS_NOTFOUND;
     803  	  else
     804  	    return status;
     805  	}
     806      }
     807    return NSS_STATUS_SUCCESS;
     808  }
     809  
     810  enum nss_status
     811  _nss_compat_getpwnam_r (const char *name, struct passwd *pwd,
     812  			char *buffer, size_t buflen, int *errnop)
     813  {
     814    enum nss_status result;
     815    ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 },
     816  		{ NULL, NULL, 0, 0, NULL, NULL, NULL }};
     817  
     818    if (name[0] == '-' || name[0] == '+')
     819      return NSS_STATUS_NOTFOUND;
     820  
     821    __libc_lock_lock (lock);
     822  
     823    if (ni == NULL)
     824      init_nss_interface ();
     825  
     826    __libc_lock_unlock (lock);
     827  
     828    result = internal_setpwent (&ent, 0, 0);
     829  
     830    if (result == NSS_STATUS_SUCCESS)
     831      result = internal_getpwnam_r (name, pwd, &ent, buffer, buflen, errnop);
     832  
     833    internal_endpwent_noerror (&ent);
     834  
     835    return result;
     836  }
     837  
     838  /* This function handle the + entry in /etc/passwd for getpwuid */
     839  static enum nss_status
     840  getpwuid_plususer (uid_t uid, struct passwd *result, char *buffer,
     841  		   size_t buflen, int *errnop)
     842  {
     843    struct passwd pwd;
     844    char *p;
     845    size_t plen;
     846  
     847    if (!getpwuid_r_impl)
     848      return NSS_STATUS_UNAVAIL;
     849  
     850    memset (&pwd, '\0', sizeof (struct passwd));
     851  
     852    copy_pwd_changes (&pwd, result, NULL, 0);
     853  
     854    plen = pwd_need_buflen (&pwd);
     855    if (plen > buflen)
     856      {
     857        *errnop = ERANGE;
     858        return NSS_STATUS_TRYAGAIN;
     859      }
     860    p = buffer + (buflen - plen);
     861    buflen -= plen;
     862  
     863    if (getpwuid_r_impl (uid, result, buffer, buflen, errnop) ==
     864        NSS_STATUS_SUCCESS)
     865      {
     866        copy_pwd_changes (result, &pwd, p, plen);
     867        give_pwd_free (&pwd);
     868        /* We found the entry.  */
     869        return NSS_STATUS_SUCCESS;
     870      }
     871    else
     872      {
     873        /* Give buffer the old len back */
     874        buflen += plen;
     875        give_pwd_free (&pwd);
     876      }
     877    return NSS_STATUS_RETURN;
     878  }
     879  
     880  /* Searches in /etc/passwd and the NSS subsystem for a special user id */
     881  static enum nss_status
     882  internal_getpwuid_r (uid_t uid, struct passwd *result, ent_t *ent,
     883  		     char *buffer, size_t buflen, int *errnop)
     884  {
     885    struct parser_data *data = (void *) buffer;
     886  
     887    while (1)
     888      {
     889        fpos_t pos;
     890        char *p;
     891        int parse_res;
     892  
     893        do
     894  	{
     895  	  /* We need at least 3 characters for one line.  */
     896  	  if (__glibc_unlikely (buflen < 3))
     897  	    {
     898  	    erange:
     899  	      *errnop = ERANGE;
     900  	      return NSS_STATUS_TRYAGAIN;
     901  	    }
     902  
     903  	  fgetpos (ent->stream, &pos);
     904  	  buffer[buflen - 1] = '\xff';
     905  	  p = fgets_unlocked (buffer, buflen, ent->stream);
     906  	  if (p == NULL && feof_unlocked (ent->stream))
     907  	    return NSS_STATUS_NOTFOUND;
     908  
     909  	  if (p == NULL || __builtin_expect (buffer[buflen - 1] != '\xff', 0))
     910  	    {
     911  	    erange_reset:
     912  	      fsetpos (ent->stream, &pos);
     913  	      goto erange;
     914  	    }
     915  
     916  	  /* Terminate the line for any case.  */
     917  	  buffer[buflen - 1] = '\0';
     918  
     919  	  /* Skip leading blanks.  */
     920  	  while (isspace (*p))
     921  	    ++p;
     922  	}
     923        while (*p == '\0' || *p == '#' /* Ignore empty and comment lines.  */
     924  	     /* Parse the line.  If it is invalid, loop to
     925  	        get the next line of the file to parse.  */
     926  	     || !(parse_res = _nss_files_parse_pwent (p, result, data, buflen,
     927  						      errnop)));
     928  
     929        if (__glibc_unlikely (parse_res == -1))
     930  	/* The parser ran out of space.  */
     931  	goto erange_reset;
     932  
     933        /* This is a real entry.  */
     934        if (result->pw_name[0] != '+' && result->pw_name[0] != '-')
     935  	{
     936  	  if (result->pw_uid == uid)
     937  	    return NSS_STATUS_SUCCESS;
     938  	  else
     939  	    continue;
     940  	}
     941  
     942        /* -@netgroup */
     943        if (result->pw_name[0] == '-' && result->pw_name[1] == '@'
     944  	  && result->pw_name[2] != '\0')
     945  	{
     946  	  /* -1, because we remove first two character of pw_name.  */
     947  	  size_t len = strlen (result->pw_name) - 1;
     948  	  char buf[len];
     949  	  enum nss_status status;
     950  
     951  	  memcpy (buf, &result->pw_name[2], len);
     952  
     953  	  status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
     954  	  if (status == NSS_STATUS_SUCCESS
     955  	      && innetgr (buf, NULL, result->pw_name, NULL))
     956  	    return NSS_STATUS_NOTFOUND;
     957  
     958  	  continue;
     959  	}
     960  
     961        /* +@netgroup */
     962        if (result->pw_name[0] == '+' && result->pw_name[1] == '@'
     963  	  && result->pw_name[2] != '\0')
     964  	{
     965  	  /* -1, because we remove first two characters of pw_name.  */
     966  	  size_t len = strlen (result->pw_name) - 1;
     967  	  char buf[len];
     968  	  enum nss_status status;
     969  
     970  	  memcpy (buf, &result->pw_name[2], len);
     971  
     972  	  status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
     973  
     974  	  if (status == NSS_STATUS_RETURN)
     975  	    continue;
     976  
     977  	  if (status == NSS_STATUS_SUCCESS)
     978  	    {
     979  	      if (innetgr (buf, NULL, result->pw_name, NULL))
     980  		return NSS_STATUS_SUCCESS;
     981  	    }
     982  	  else if (status == NSS_STATUS_RETURN)	/* We couldn't parse the entry */
     983  	    return NSS_STATUS_NOTFOUND;
     984  	  else
     985  	    return status;
     986  
     987  	  continue;
     988  	}
     989  
     990        /* -user */
     991        if (result->pw_name[0] == '-' && result->pw_name[1] != '\0'
     992  	  && result->pw_name[1] != '@')
     993  	{
     994  	  size_t len = strlen (result->pw_name);
     995  	  char buf[len];
     996  	  enum nss_status status;
     997  
     998  	  memcpy (buf, &result->pw_name[1], len);
     999  
    1000  	  status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
    1001  	  if (status == NSS_STATUS_SUCCESS
    1002  	      && innetgr (buf, NULL, result->pw_name, NULL))
    1003  	    return NSS_STATUS_NOTFOUND;
    1004  	  continue;
    1005  	}
    1006  
    1007        /* +user */
    1008        if (result->pw_name[0] == '+' && result->pw_name[1] != '\0'
    1009  	  && result->pw_name[1] != '@')
    1010  	{
    1011  	  size_t len = strlen (result->pw_name);
    1012  	  char buf[len];
    1013  	  enum nss_status status;
    1014  
    1015  	  memcpy (buf, &result->pw_name[1], len);
    1016  
    1017  	  status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
    1018  
    1019  	  if (status == NSS_STATUS_RETURN)
    1020  	    continue;
    1021  
    1022  	  if (status == NSS_STATUS_SUCCESS)
    1023  	    {
    1024  	      if (strcmp (buf, result->pw_name) == 0)
    1025  		return NSS_STATUS_SUCCESS;
    1026  	    }
    1027  	  else if (status == NSS_STATUS_RETURN)	/* We couldn't parse the entry */
    1028  	    return NSS_STATUS_NOTFOUND;
    1029  	  else
    1030  	    return status;
    1031  
    1032  	  continue;
    1033  	}
    1034  
    1035        /* +:... */
    1036        if (result->pw_name[0] == '+' && result->pw_name[1] == '\0')
    1037  	{
    1038  	  enum nss_status status;
    1039  
    1040  	  status = getpwuid_plususer (uid, result, buffer, buflen, errnop);
    1041  	  if (status == NSS_STATUS_SUCCESS)	/* We found the entry. */
    1042  	    break;
    1043  	  else if (status == NSS_STATUS_RETURN)	/* We couldn't parse the entry */
    1044  	    return NSS_STATUS_NOTFOUND;
    1045  	  else
    1046  	    return status;
    1047  	}
    1048      }
    1049    return NSS_STATUS_SUCCESS;
    1050  }
    1051  
    1052  enum nss_status
    1053  _nss_compat_getpwuid_r (uid_t uid, struct passwd *pwd,
    1054  			char *buffer, size_t buflen, int *errnop)
    1055  {
    1056    enum nss_status result;
    1057    ent_t ent = { false, false, true, NSS_STATUS_SUCCESS, NULL, { NULL, 0, 0 },
    1058  		{ NULL, NULL, 0, 0, NULL, NULL, NULL }};
    1059  
    1060    __libc_lock_lock (lock);
    1061  
    1062    if (ni == NULL)
    1063      init_nss_interface ();
    1064  
    1065    __libc_lock_unlock (lock);
    1066  
    1067    result = internal_setpwent (&ent, 0, 0);
    1068  
    1069    if (result == NSS_STATUS_SUCCESS)
    1070      result = internal_getpwuid_r (uid, pwd, &ent, buffer, buflen, errnop);
    1071  
    1072    internal_endpwent_noerror (&ent);
    1073  
    1074    return result;
    1075  }
    1076  
    1077  
    1078  /* Support routines for remembering -@netgroup and -user entries.
    1079     The names are stored in a single string with `|' as separator. */
    1080  static void
    1081  blacklist_store_name (const char *name, ent_t *ent)
    1082  {
    1083    int namelen = strlen (name);
    1084    char *tmp;
    1085  
    1086    /* first call, setup cache */
    1087    if (ent->blacklist.size == 0)
    1088      {
    1089        ent->blacklist.size = MAX (BLACKLIST_INITIAL_SIZE, 2 * namelen);
    1090        ent->blacklist.data = malloc (ent->blacklist.size);
    1091        if (ent->blacklist.data == NULL)
    1092  	return;
    1093        ent->blacklist.data[0] = '|';
    1094        ent->blacklist.data[1] = '\0';
    1095        ent->blacklist.current = 1;
    1096      }
    1097    else
    1098      {
    1099        if (in_blacklist (name, namelen, ent))
    1100  	return;			/* no duplicates */
    1101  
    1102        if (ent->blacklist.current + namelen + 1 >= ent->blacklist.size)
    1103  	{
    1104  	  ent->blacklist.size += MAX (BLACKLIST_INCREMENT, 2 * namelen);
    1105  	  tmp = realloc (ent->blacklist.data, ent->blacklist.size);
    1106  	  if (tmp == NULL)
    1107  	    {
    1108  	      free (ent->blacklist.data);
    1109  	      ent->blacklist.size = 0;
    1110  	      return;
    1111  	    }
    1112  	  ent->blacklist.data = tmp;
    1113  	}
    1114      }
    1115  
    1116    tmp = stpcpy (ent->blacklist.data + ent->blacklist.current, name);
    1117    *tmp++ = '|';
    1118    *tmp = '\0';
    1119    ent->blacklist.current += namelen + 1;
    1120  
    1121    return;
    1122  }
    1123  
    1124  /* Returns whether ent->blacklist contains name.  */
    1125  static bool
    1126  in_blacklist (const char *name, int namelen, ent_t *ent)
    1127  {
    1128    char buf[namelen + 3];
    1129    char *cp;
    1130  
    1131    if (ent->blacklist.data == NULL)
    1132      return false;
    1133  
    1134    buf[0] = '|';
    1135    cp = stpcpy (&buf[1], name);
    1136    *cp++ = '|';
    1137    *cp = '\0';
    1138    return strstr (ent->blacklist.data, buf) != NULL;
    1139  }