(root)/
glibc-2.38/
nss/
nss_files/
files-alias.c
       1  /* Mail alias file parser in nss_files module.
       2     Copyright (C) 1996-2023 Free Software Foundation, Inc.
       3     This file is part of the GNU C Library.
       4  
       5     The GNU C Library is free software; you can redistribute it and/or
       6     modify it under the terms of the GNU Lesser General Public
       7     License as published by the Free Software Foundation; either
       8     version 2.1 of the License, or (at your option) any later version.
       9  
      10     The GNU C Library is distributed in the hope that it will be useful,
      11     but WITHOUT ANY WARRANTY; without even the implied warranty of
      12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13     Lesser General Public License for more details.
      14  
      15     You should have received a copy of the GNU Lesser General Public
      16     License along with the GNU C Library; if not, see
      17     <https://www.gnu.org/licenses/>.  */
      18  
      19  #include <aliases.h>
      20  #include <ctype.h>
      21  #include <errno.h>
      22  #include <fcntl.h>
      23  #include <libc-lock.h>
      24  #include <stdlib.h>
      25  #include <stdio.h>
      26  #include <string.h>
      27  
      28  #include <kernel-features.h>
      29  
      30  #include "nsswitch.h"
      31  #include <nss_files.h>
      32  
      33  
      34  /* Maintenance of the stream open on the database file.  For getXXent
      35     operations the stream needs to be held open across calls, the other
      36     getXXbyYY operations all use their own stream.  */
      37  
      38  static enum nss_status
      39  internal_setent (FILE **stream)
      40  {
      41    enum nss_status status = NSS_STATUS_SUCCESS;
      42  
      43    if (*stream == NULL)
      44      {
      45        *stream = __nss_files_fopen ("/etc/aliases");
      46  
      47        if (*stream == NULL)
      48  	status = errno == EAGAIN ? NSS_STATUS_TRYAGAIN : NSS_STATUS_UNAVAIL;
      49      }
      50    else
      51      rewind (*stream);
      52  
      53    return status;
      54  }
      55  
      56  
      57  /* Thread-safe, exported version of that.  */
      58  enum nss_status
      59  _nss_files_setaliasent (void)
      60  {
      61    return __nss_files_data_setent (nss_file_aliasent, "/etc/aliases");
      62  }
      63  libc_hidden_def (_nss_files_setaliasent)
      64  
      65  enum nss_status
      66  _nss_files_endaliasent (void)
      67  {
      68    return __nss_files_data_endent (nss_file_aliasent);
      69  }
      70  libc_hidden_def (_nss_files_endaliasent)
      71  
      72  /* Parsing the database file into `struct aliasent' data structures.  */
      73  static enum nss_status
      74  get_next_alias (FILE *stream, const char *match, struct aliasent *result,
      75  		char *buffer, size_t buflen, int *errnop)
      76  {
      77    enum nss_status status = NSS_STATUS_NOTFOUND;
      78    int ignore = 0;
      79  
      80    result->alias_members_len = 0;
      81  
      82    while (1)
      83      {
      84        /* Now we are ready to process the input.  We have to read a
      85  	 line and all its continuations and construct the array of
      86  	 string pointers.  This pointers and the names itself have to
      87  	 be placed in BUFFER.  */
      88        char *first_unused = buffer;
      89        size_t room_left = buflen - (buflen % __alignof__ (char *));
      90        char *line;
      91  
      92        /* Check whether the buffer is large enough for even trying to
      93  	 read something.  */
      94        if (room_left < 2)
      95  	goto no_more_room;
      96  
      97        /* Read the first line.  It must contain the alias name and
      98  	 possibly some alias names.  */
      99        first_unused[room_left - 1] = '\xff';
     100        line = __fgets_unlocked (first_unused, room_left, stream);
     101        if (line == NULL)
     102  	/* Nothing to read.  */
     103  	break;
     104        else if (first_unused[room_left - 1] != '\xff')
     105  	{
     106  	  /* The line is too long for our buffer.  */
     107  	no_more_room:
     108  	  *errnop = ERANGE;
     109  	  status = NSS_STATUS_TRYAGAIN;
     110  	  break;
     111  	}
     112        else
     113  	{
     114  	  char *cp;
     115  
     116  	  /* If we are in IGNORE mode and the first character in the
     117  	     line is a white space we ignore the line and start
     118  	     reading the next.  */
     119  	  if (ignore && isspace (*first_unused))
     120  	    continue;
     121  
     122  	  /* Terminate the line for any case.  */
     123  	  cp = strpbrk (first_unused, "#\n");
     124  	  if (cp != NULL)
     125  	    *cp = '\0';
     126  
     127  	  /* Skip leading blanks.  */
     128  	  while (isspace (*line))
     129  	    ++line;
     130  
     131  	  result->alias_name = first_unused;
     132  	  while (*line != '\0' && *line != ':')
     133  	    *first_unused++ = *line++;
     134  	  if (*line == '\0' || result->alias_name == first_unused)
     135  	    /* No valid name.  Ignore the line.  */
     136  	    continue;
     137  
     138  	  *first_unused++ = '\0';
     139  	  if (room_left < (size_t) (first_unused - result->alias_name))
     140  	    goto no_more_room;
     141  	  room_left -= first_unused - result->alias_name;
     142  	  ++line;
     143  
     144  	  /* When we search for a specific alias we can avoid all the
     145  	     difficult parts and compare now with the name we are
     146  	     looking for.  If it does not match we simply ignore all
     147  	     lines until the next line containing the start of a new
     148  	     alias is found.  */
     149  	  ignore = (match != NULL
     150  		    && __strcasecmp (result->alias_name, match) != 0);
     151  
     152  	  while (! ignore)
     153  	    {
     154  	      while (isspace (*line))
     155  		++line;
     156  
     157  	      cp = first_unused;
     158  	      while (*line != '\0' && *line != ',')
     159  		*first_unused++ = *line++;
     160  
     161  	      if (first_unused != cp)
     162  		{
     163  		  /* OK, we can have a regular entry or an include
     164  		     request.  */
     165  		  if (*line != '\0')
     166  		    ++line;
     167  		  *first_unused++ = '\0';
     168  
     169  		  if (strncmp (cp, ":include:", 9) != 0)
     170  		    {
     171  		      if (room_left < (first_unused - cp) + sizeof (char *))
     172  			goto no_more_room;
     173  		      room_left -= (first_unused - cp) + sizeof (char *);
     174  
     175  		      ++result->alias_members_len;
     176  		    }
     177  		  else
     178  		    {
     179  		      /* Oh well, we have to read the addressed file.  */
     180  		      FILE *listfile;
     181  		      char *old_line = NULL;
     182  
     183  		      first_unused = cp;
     184  
     185  		      listfile = __nss_files_fopen (&cp[9]);
     186  		      /* If the file does not exist we simply ignore
     187  			 the statement.  */
     188  		      if (listfile != NULL
     189  			  && (old_line = __strdup (line)) != NULL)
     190  			{
     191  			  while (! __feof_unlocked (listfile))
     192  			    {
     193  			      if (room_left < 2)
     194  				{
     195  				  free (old_line);
     196  				  fclose (listfile);
     197  				  goto no_more_room;
     198  				}
     199  
     200  			      first_unused[room_left - 1] = '\xff';
     201  			      line = __fgets_unlocked (first_unused, room_left,
     202  						       listfile);
     203  			      if (line == NULL)
     204  				break;
     205  			      if (first_unused[room_left - 1] != '\xff')
     206  				{
     207  				  free (old_line);
     208  				  fclose (listfile);
     209  				  goto no_more_room;
     210  				}
     211  
     212  			      /* Parse the line.  */
     213  			      cp = strpbrk (line, "#\n");
     214  			      if (cp != NULL)
     215  				*cp = '\0';
     216  
     217  			      do
     218  				{
     219  				  while (isspace (*line))
     220  				    ++line;
     221  
     222  				  cp = first_unused;
     223  				  while (*line != '\0' && *line != ',')
     224  				    *first_unused++ = *line++;
     225  
     226  				  if (*line != '\0')
     227  				    ++line;
     228  
     229  				  if (first_unused != cp)
     230  				    {
     231  				      *first_unused++ = '\0';
     232  				      if (room_left < ((first_unused - cp)
     233  						       + __alignof__ (char *)))
     234  					{
     235  					  free (old_line);
     236  					  fclose (listfile);
     237  					  goto no_more_room;
     238  					}
     239  				      room_left -= ((first_unused - cp)
     240  						    + __alignof__ (char *));
     241  				      ++result->alias_members_len;
     242  				    }
     243  				}
     244  			      while (*line != '\0');
     245  			    }
     246  			  fclose (listfile);
     247  
     248  			  first_unused[room_left - 1] = '\0';
     249  			  strncpy (first_unused, old_line, room_left);
     250  
     251  			  free (old_line);
     252  			  line = first_unused;
     253  
     254  			  if (first_unused[room_left - 1] != '\0')
     255  			    goto no_more_room;
     256  			}
     257  		    }
     258  		}
     259  
     260  	      if (*line == '\0')
     261  		{
     262  		  /* Get the next line.  But we must be careful.  We
     263  		     must not read the whole line at once since it
     264  		     might belong to the current alias.  Simply read
     265  		     the first character.  If it is a white space we
     266  		     have a continuation line.  Otherwise it is the
     267  		     beginning of a new alias and we can push back the
     268  		     just read character.  */
     269  		  int ch;
     270  
     271  		  ch = __getc_unlocked (stream);
     272  		  if (ch == EOF || ch == '\n' || !isspace (ch))
     273  		    {
     274  		      size_t cnt;
     275  
     276  		      /* Now prepare the return.  Provide string
     277  			 pointers for the currently selected aliases.  */
     278  		      if (ch != EOF)
     279  			ungetc (ch, stream);
     280  
     281  		      /* Adjust the pointer so it is aligned for
     282  			 storing pointers.  */
     283  		      first_unused += __alignof__ (char *) - 1;
     284  		      first_unused -= (((uintptr_t) first_unused)
     285  				       % __alignof__ (char *));
     286  		      result->alias_members = (char **) first_unused;
     287  
     288  		      /* Compute addresses of alias entry strings.  */
     289  		      cp = result->alias_name;
     290  		      for (cnt = 0; cnt < result->alias_members_len; ++cnt)
     291  			{
     292  			  cp = strchr (cp, '\0') + 1;
     293  			  result->alias_members[cnt] = cp;
     294  			}
     295  
     296  		      status = (result->alias_members_len == 0
     297  				? NSS_STATUS_RETURN : NSS_STATUS_SUCCESS);
     298  		      break;
     299  		    }
     300  
     301  		  /* The just read character is a white space and so
     302  		     can be ignored.  */
     303  		  first_unused[room_left - 1] = '\xff';
     304  		  line = __fgets_unlocked (first_unused, room_left, stream);
     305  		  if (line == NULL)
     306  		    {
     307  		      /* Continuation line without any data and
     308  			 without a newline at the end.  Treat it as an
     309  			 empty line and retry, reaching EOF once
     310  			 more.  */
     311  		      line = first_unused;
     312  		      *line = '\0';
     313  		      continue;
     314  		    }
     315  		  if (first_unused[room_left - 1] != '\xff')
     316  		    goto no_more_room;
     317  		  cp = strpbrk (line, "#\n");
     318  		  if (cp != NULL)
     319  		    *cp = '\0';
     320  		}
     321  	    }
     322  	}
     323  
     324        if (status != NSS_STATUS_NOTFOUND)
     325  	/* We read something.  In any case break here.  */
     326  	break;
     327      }
     328  
     329    return status;
     330  }
     331  
     332  
     333  enum nss_status
     334  _nss_files_getaliasent_r (struct aliasent *result, char *buffer, size_t buflen,
     335  			  int *errnop)
     336  {
     337    /* Return next entry in host file.  */
     338  
     339    struct nss_files_per_file_data *data;
     340    enum nss_status status = __nss_files_data_open (&data, nss_file_aliasent,
     341  						  "/etc/aliases", errnop, NULL);
     342    if (status != NSS_STATUS_SUCCESS)
     343      return status;
     344  
     345    result->alias_local = 1;
     346  
     347    /* Read lines until we get a definite result.  */
     348    do
     349      status = get_next_alias (data->stream, NULL, result, buffer, buflen,
     350  			     errnop);
     351    while (status == NSS_STATUS_RETURN);
     352  
     353    __nss_files_data_put (data);
     354    return status;
     355  }
     356  libc_hidden_def (_nss_files_getaliasent_r)
     357  
     358  enum nss_status
     359  _nss_files_getaliasbyname_r (const char *name, struct aliasent *result,
     360  			     char *buffer, size_t buflen, int *errnop)
     361  {
     362    /* Return next entry in host file.  */
     363    enum nss_status status = NSS_STATUS_SUCCESS;
     364    FILE *stream = NULL;
     365  
     366    if (name == NULL)
     367      {
     368        __set_errno (EINVAL);
     369        return NSS_STATUS_UNAVAIL;
     370      }
     371  
     372    /* Open the stream.  */
     373    status = internal_setent (&stream);
     374  
     375    if (status == NSS_STATUS_SUCCESS)
     376      {
     377        result->alias_local = 1;
     378  
     379        /* Read lines until we get a definite result.  */
     380        do
     381  	status = get_next_alias (stream, name, result, buffer, buflen, errnop);
     382        while (status == NSS_STATUS_RETURN);
     383  
     384        fclose (stream);
     385      }
     386  
     387    return status;
     388  }
     389  libc_hidden_def (_nss_files_getaliasbyname_r)