(root)/
glibc-2.38/
inet/
getnetgrent_r.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 <assert.h>
      19  #include <atomic.h>
      20  #include <libc-lock.h>
      21  #include <errno.h>
      22  #include <netdb.h>
      23  #include <stdbool.h>
      24  #include <stdlib.h>
      25  #include <string.h>
      26  #include "netgroup.h"
      27  #include "nsswitch.h"
      28  #include <sysdep.h>
      29  #include <nscd/nscd_proto.h>
      30  
      31  
      32  /* Protect above variable against multiple uses at the same time.  */
      33  __libc_lock_define_initialized (static, lock)
      34  
      35  /* The whole information for the set/get/endnetgrent functions are
      36     kept in this structure.  */
      37  static struct __netgrent dataset;
      38  
      39  /* Set up NIP to run through the services.  Return nonzero if there are no
      40     services (left).  */
      41  static int
      42  setup (void **fctp, nss_action_list *nipp)
      43  {
      44    int no_more;
      45  
      46    no_more = __nss_netgroup_lookup2 (nipp, "setnetgrent", NULL, fctp);
      47  
      48    return no_more;
      49  }
      50  
      51  /* Free used memory.  */
      52  static void
      53  free_memory (struct __netgrent *data)
      54  {
      55    while (data->known_groups != NULL)
      56      {
      57        struct name_list *tmp = data->known_groups;
      58        data->known_groups = data->known_groups->next;
      59        free (tmp);
      60      }
      61  
      62    while (data->needed_groups != NULL)
      63      {
      64        struct name_list *tmp = data->needed_groups;
      65        data->needed_groups = data->needed_groups->next;
      66        free (tmp);
      67      }
      68  }
      69  
      70  static void
      71  endnetgrent_hook (struct __netgrent *datap)
      72  {
      73    enum nss_status (*endfct) (struct __netgrent *);
      74  
      75    if (datap->nip == NULL || datap->nip == (nss_action_list) -1l)
      76      return;
      77  
      78    endfct = __nss_lookup_function (datap->nip, "endnetgrent");
      79    if (endfct != NULL)
      80      (void) (*endfct) (datap);
      81    datap->nip = NULL;
      82  }
      83  
      84  static int
      85  __internal_setnetgrent_reuse (const char *group, struct __netgrent *datap,
      86  			      int *errnop)
      87  {
      88    union
      89    {
      90      enum nss_status (*f) (const char *, struct __netgrent *);
      91      void *ptr;
      92    } fct;
      93    enum nss_status status = NSS_STATUS_UNAVAIL;
      94    struct name_list *new_elem;
      95  
      96    /* Free data from previous service.  */
      97    endnetgrent_hook (datap);
      98  
      99    /* Cycle through all the services and run their setnetgrent functions.  */
     100    int no_more = setup (&fct.ptr, &datap->nip);
     101    while (! no_more)
     102      {
     103        assert (datap->data == NULL);
     104  
     105        /* Ignore status, we force check in `__nss_next2'.  */
     106        status = DL_CALL_FCT (*fct.f, (group, datap));
     107  
     108        nss_action_list old_nip = datap->nip;
     109        no_more = __nss_next2 (&datap->nip, "setnetgrent", NULL, &fct.ptr,
     110  			     status, 0);
     111  
     112        if (status == NSS_STATUS_SUCCESS && ! no_more)
     113  	{
     114  	  enum nss_status (*endfct) (struct __netgrent *);
     115  
     116  	  endfct = __nss_lookup_function (old_nip, "endnetgrent");
     117  	  if (endfct != NULL)
     118  	    (void) DL_CALL_FCT (*endfct, (datap));
     119  	}
     120      }
     121  
     122    /* Add the current group to the list of known groups.  */
     123    size_t group_len = strlen (group) + 1;
     124    new_elem = (struct name_list *) malloc (sizeof (struct name_list)
     125  					  + group_len);
     126    if (new_elem == NULL)
     127      {
     128        *errnop = errno;
     129        status = NSS_STATUS_TRYAGAIN;
     130      }
     131    else
     132      {
     133        new_elem->next = datap->known_groups;
     134        memcpy (new_elem->name, group, group_len);
     135        datap->known_groups = new_elem;
     136      }
     137  
     138    return status == NSS_STATUS_SUCCESS;
     139  }
     140  
     141  int
     142  __internal_setnetgrent (const char *group, struct __netgrent *datap)
     143  {
     144    /* Free list of all netgroup names from last run.  */
     145    free_memory (datap);
     146  
     147    return __internal_setnetgrent_reuse (group, datap, &errno);
     148  }
     149  libc_hidden_def (__internal_setnetgrent)
     150  
     151  static int
     152  nscd_setnetgrent (const char *group)
     153  {
     154  #ifdef USE_NSCD
     155    if (__nss_not_use_nscd_netgroup > 0
     156        && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
     157      __nss_not_use_nscd_netgroup = 0;
     158  
     159    if (!__nss_not_use_nscd_netgroup
     160        && !__nss_database_custom[NSS_DBSIDX_netgroup])
     161      return __nscd_setnetgrent (group, &dataset);
     162  #endif
     163    return -1;
     164  }
     165  
     166  int
     167  setnetgrent (const char *group)
     168  {
     169    int result;
     170  
     171    __libc_lock_lock (lock);
     172  
     173    result = nscd_setnetgrent (group);
     174    if (result < 0)
     175      result = __internal_setnetgrent (group, &dataset);
     176  
     177    __libc_lock_unlock (lock);
     178  
     179    return result;
     180  }
     181  
     182  void
     183  __internal_endnetgrent (struct __netgrent *datap)
     184  {
     185    endnetgrent_hook (datap);
     186    /* Now free list of all netgroup names from last run.  */
     187    free_memory (datap);
     188  }
     189  libc_hidden_def (__internal_endnetgrent)
     190  
     191  
     192  void
     193  endnetgrent (void)
     194  {
     195    __libc_lock_lock (lock);
     196  
     197    __internal_endnetgrent (&dataset);
     198  
     199    __libc_lock_unlock (lock);
     200  }
     201  
     202  #ifdef USE_NSCD
     203  static const char *
     204  get_nonempty_val (const char *in)
     205  {
     206    if (*in == '\0')
     207      return NULL;
     208    return in;
     209  }
     210  
     211  static enum nss_status
     212  nscd_getnetgrent (struct __netgrent *datap, char *buffer, size_t buflen,
     213  		  int *errnop)
     214  {
     215    if (datap->cursor >= datap->data + datap->data_size)
     216      return NSS_STATUS_UNAVAIL;
     217  
     218    datap->type = triple_val;
     219    datap->val.triple.host = get_nonempty_val (datap->cursor);
     220    datap->cursor = strchr (datap->cursor, '\0') + 1;
     221    datap->val.triple.user = get_nonempty_val (datap->cursor);
     222    datap->cursor = strchr (datap->cursor, '\0') + 1;
     223    datap->val.triple.domain = get_nonempty_val (datap->cursor);
     224    datap->cursor = strchr (datap->cursor, '\0') + 1;
     225  
     226    return NSS_STATUS_SUCCESS;
     227  }
     228  #endif
     229  
     230  int
     231  __internal_getnetgrent_r (char **hostp, char **userp, char **domainp,
     232  			  struct __netgrent *datap,
     233  			  char *buffer, size_t buflen, int *errnop)
     234  {
     235    enum nss_status (*fct) (struct __netgrent *, char *, size_t, int *);
     236  
     237    /* Initialize status to return if no more functions are found.  */
     238    enum nss_status status = NSS_STATUS_NOTFOUND;
     239  
     240    /* Run through available functions, starting with the same function last
     241       run.  We will repeat each function as long as it succeeds, and then go
     242       on to the next service action.  */
     243    int no_more = datap->nip == NULL;
     244    if (! no_more)
     245      {
     246  #ifdef USE_NSCD
     247        /* This bogus function pointer is a special marker left by
     248  	 __nscd_setnetgrent to tell us to use the data it left
     249  	 before considering any modules.  */
     250        if (datap->nip == (nss_action_list) -1l)
     251  	fct = nscd_getnetgrent;
     252        else
     253  #endif
     254  	{
     255  	  fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
     256  	  no_more = fct == NULL;
     257  	}
     258  
     259        while (! no_more)
     260  	{
     261  	  status = DL_CALL_FCT (*fct, (datap, buffer, buflen, &errno));
     262  
     263  	  if (status == NSS_STATUS_RETURN
     264  	      /* The service returned a NOTFOUND, but there are more groups that
     265  		 we need to resolve before we give up.  */
     266  	      || (status == NSS_STATUS_NOTFOUND && datap->needed_groups != NULL))
     267  	    {
     268  	      /* This was the last one for this group.  Look at next group
     269  		 if available.  */
     270  	      int found = 0;
     271  	      while (datap->needed_groups != NULL && ! found)
     272  		{
     273  		  struct name_list *tmp = datap->needed_groups;
     274  		  datap->needed_groups = datap->needed_groups->next;
     275  		  tmp->next = datap->known_groups;
     276  		  datap->known_groups = tmp;
     277  
     278  		  found = __internal_setnetgrent_reuse (datap->known_groups->name,
     279  							datap, errnop);
     280  		}
     281  
     282  	      if (found && datap->nip != NULL)
     283  		{
     284  		  fct = __nss_lookup_function (datap->nip, "getnetgrent_r");
     285  		  if (fct != NULL)
     286  		    continue;
     287  		}
     288  	    }
     289  	  else if (status == NSS_STATUS_SUCCESS && datap->type == group_val)
     290  	    {
     291  	      /* The last entry was a name of another netgroup.  */
     292  	      struct name_list *namep;
     293  
     294  	      /* Ignore if we've seen the name before.  */
     295  	      for (namep = datap->known_groups; namep != NULL;
     296  		   namep = namep->next)
     297  		if (strcmp (datap->val.group, namep->name) == 0)
     298  		  break;
     299  	      if (namep == NULL)
     300  		for (namep = datap->needed_groups; namep != NULL;
     301  		     namep = namep->next)
     302  		  if (strcmp (datap->val.group, namep->name) == 0)
     303  		    break;
     304  	      if (namep != NULL)
     305  		/* Really ignore.  */
     306  		continue;
     307  
     308  	      size_t group_len = strlen (datap->val.group) + 1;
     309  	      namep = (struct name_list *) malloc (sizeof (struct name_list)
     310  						  + group_len);
     311  	      if (namep == NULL)
     312  		/* We are out of memory.  */
     313  		status = NSS_STATUS_RETURN;
     314  	      else
     315  		{
     316  		  namep->next = datap->needed_groups;
     317  		  memcpy (namep->name, datap->val.group, group_len);
     318  		  datap->needed_groups = namep;
     319  		  /* And get the next entry.  */
     320  		  continue;
     321  		}
     322  	    }
     323  	  break;
     324  	}
     325      }
     326  
     327    if (status == NSS_STATUS_SUCCESS)
     328      {
     329        *hostp = (char *) datap->val.triple.host;
     330        *userp = (char *) datap->val.triple.user;
     331        *domainp = (char *) datap->val.triple.domain;
     332      }
     333  
     334    return status == NSS_STATUS_SUCCESS ? 1 : 0;
     335  }
     336  libc_hidden_def (__internal_getnetgrent_r)
     337  
     338  /* The real entry point.  */
     339  int
     340  __getnetgrent_r (char **hostp, char **userp, char **domainp,
     341  		 char *buffer, size_t buflen)
     342  {
     343    enum nss_status status;
     344  
     345    __libc_lock_lock (lock);
     346  
     347    status = __internal_getnetgrent_r (hostp, userp, domainp, &dataset,
     348  				     buffer, buflen, &errno);
     349  
     350    __libc_lock_unlock (lock);
     351  
     352    return status;
     353  }
     354  weak_alias (__getnetgrent_r, getnetgrent_r)
     355  
     356  /* Test whether given (host,user,domain) triple is in NETGROUP.  */
     357  int
     358  innetgr (const char *netgroup, const char *host, const char *user,
     359  	 const char *domain)
     360  {
     361  #ifdef USE_NSCD
     362    if (__nss_not_use_nscd_netgroup > 0
     363        && ++__nss_not_use_nscd_netgroup > NSS_NSCD_RETRY)
     364      __nss_not_use_nscd_netgroup = 0;
     365  
     366    if (!__nss_not_use_nscd_netgroup
     367        && !__nss_database_custom[NSS_DBSIDX_netgroup])
     368      {
     369        int result = __nscd_innetgr (netgroup, host, user, domain);
     370        if (result >= 0)
     371  	return result;
     372      }
     373  #endif
     374  
     375    union
     376    {
     377      enum nss_status (*f) (const char *, struct __netgrent *);
     378      void *ptr;
     379    } setfct;
     380    void (*endfct) (struct __netgrent *);
     381    int (*getfct) (struct __netgrent *, char *, size_t, int *);
     382    struct __netgrent entry;
     383    int result = 0;
     384    const char *current_group = netgroup;
     385  
     386    memset (&entry, '\0', sizeof (entry));
     387  
     388    /* Walk through the services until we found an answer or we shall
     389       not work further.  We can do some optimization here.  Since all
     390       services must provide the `setnetgrent' function we can do all
     391       the work during one walk through the service list.  */
     392    while (1)
     393      {
     394        int no_more = setup (&setfct.ptr, &entry.nip);
     395        while (! no_more)
     396  	{
     397  	  assert (entry.data == NULL);
     398  
     399  	  /* Open netgroup.  */
     400  	  enum nss_status status = DL_CALL_FCT (*setfct.f,
     401  						(current_group, &entry));
     402  
     403  	  if (status == NSS_STATUS_SUCCESS
     404  	      && (getfct = __nss_lookup_function (entry.nip, "getnetgrent_r"))
     405  		 != NULL)
     406  	    {
     407  	      char buffer[1024];
     408  
     409  	      while (DL_CALL_FCT (*getfct,
     410  				  (&entry, buffer, sizeof buffer, &errno))
     411  		     == NSS_STATUS_SUCCESS)
     412  		{
     413  		  if (entry.type == group_val)
     414  		    {
     415  		      /* Make sure we haven't seen the name before.  */
     416  		      struct name_list *namep;
     417  
     418  		      for (namep = entry.known_groups; namep != NULL;
     419  			   namep = namep->next)
     420  			if (strcmp (entry.val.group, namep->name) == 0)
     421  			  break;
     422  		      if (namep == NULL)
     423  			for (namep = entry.needed_groups; namep != NULL;
     424  			     namep = namep->next)
     425  			  if (strcmp (entry.val.group, namep->name) == 0)
     426  			    break;
     427  		      if (namep == NULL
     428  			  && strcmp (netgroup, entry.val.group) != 0)
     429  			{
     430  			  size_t group_len = strlen (entry.val.group) + 1;
     431  			  namep =
     432  			    (struct name_list *) malloc (sizeof (*namep)
     433  							 + group_len);
     434  			  if (namep == NULL)
     435  			    {
     436  			      /* Out of memory, simply return.  */
     437  			      result = -1;
     438  			      break;
     439  			    }
     440  
     441  			  namep->next = entry.needed_groups;
     442  			  memcpy (namep->name, entry.val.group, group_len);
     443  			  entry.needed_groups = namep;
     444  			}
     445  		    }
     446  		  else
     447  		    {
     448  		      if ((entry.val.triple.host == NULL || host == NULL
     449  			   || __strcasecmp (entry.val.triple.host, host) == 0)
     450  			  && (entry.val.triple.user == NULL || user == NULL
     451  			      || strcmp (entry.val.triple.user, user) == 0)
     452  			  && (entry.val.triple.domain == NULL || domain == NULL
     453  			      || __strcasecmp (entry.val.triple.domain,
     454  					       domain) == 0))
     455  			{
     456  			  result = 1;
     457  			  break;
     458  			}
     459  		    }
     460  		}
     461  
     462  	      /* If we found one service which does know the given
     463  		 netgroup we don't try further.  */
     464  	      status = NSS_STATUS_RETURN;
     465  	    }
     466  
     467  	  /* Free all resources of the service.  */
     468  	  endfct = __nss_lookup_function (entry.nip, "endnetgrent");
     469  	  if (endfct != NULL)
     470  	    DL_CALL_FCT (*endfct, (&entry));
     471  
     472  	  if (result != 0)
     473  	    break;
     474  
     475  	  /* Look for the next service.  */
     476  	  no_more = __nss_next2 (&entry.nip, "setnetgrent", NULL,
     477  				 &setfct.ptr, status, 0);
     478  	}
     479  
     480        if (result == 0 && entry.needed_groups != NULL)
     481  	{
     482  	  struct name_list *tmp = entry.needed_groups;
     483  	  entry.needed_groups = tmp->next;
     484  	  tmp->next = entry.known_groups;
     485  	  entry.known_groups = tmp;
     486  	  current_group = tmp->name;
     487  	  continue;
     488  	}
     489  
     490        /* No way out.  */
     491        break;
     492      }
     493  
     494    /* Free the memory.  */
     495    free_memory (&entry);
     496  
     497    return result == 1;
     498  }
     499  libc_hidden_def (innetgr)