1  /* Copyright (c) 1997-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 <errno.h>
      19  #include <string.h>
      20  #include <rpcsvc/nis.h>
      21  #include <shlib-compat.h>
      22  
      23  nis_name
      24  nis_leaf_of (const_nis_name name)
      25  {
      26    static char result[NIS_MAXNAMELEN + 1];
      27  
      28    return nis_leaf_of_r (name, result, NIS_MAXNAMELEN);
      29  }
      30  libnsl_hidden_nolink_def (nis_leaf_of, GLIBC_2_1)
      31  
      32  nis_name
      33  nis_leaf_of_r (const_nis_name name, char *buffer, size_t buflen)
      34  {
      35    size_t i = 0;
      36  
      37    buffer[0] = '\0';
      38  
      39    while (name[i] != '.' && name[i] != '\0')
      40      i++;
      41  
      42    if (__glibc_unlikely (i >= buflen))
      43      {
      44        __set_errno (ERANGE);
      45        return NULL;
      46      }
      47  
      48    *((char *) __mempcpy (buffer, name, i)) = '\0';
      49  
      50    return buffer;
      51  }
      52  libnsl_hidden_nolink_def (nis_leaf_of_r, GLIBC_2_1)
      53  
      54  nis_name
      55  nis_name_of (const_nis_name name)
      56  {
      57    static char result[NIS_MAXNAMELEN + 1];
      58  
      59    return nis_name_of_r (name, result, NIS_MAXNAMELEN);
      60  }
      61  libnsl_hidden_nolink_def (nis_name_of, GLIBC_2_1)
      62  
      63  nis_name
      64  nis_name_of_r (const_nis_name name, char *buffer, size_t buflen)
      65  {
      66    char *local_domain;
      67    int diff;
      68  
      69    local_domain = nis_local_directory ();
      70  
      71    diff = strlen (name) - strlen (local_domain);
      72    if (diff <= 0)
      73      return NULL;
      74  
      75    if (strcmp (&name[diff], local_domain) != 0)
      76      return NULL;
      77  
      78    if ((size_t) diff >= buflen)
      79      {
      80        __set_errno (ERANGE);
      81        return NULL;
      82      }
      83  
      84    *((char *) __mempcpy (buffer, name, diff - 1)) = '\0';
      85  
      86    if (diff - 1 == 0)
      87      return NULL;
      88  
      89    return buffer;
      90  }
      91  libnsl_hidden_nolink_def (nis_name_of_r, GLIBC_2_1)
      92  
      93  static __always_inline int
      94  count_dots (const_nis_name str)
      95  {
      96    int count = 0;
      97  
      98    for (size_t i = 0; str[i] != '\0'; ++i)
      99      if (str[i] == '.')
     100        ++count;
     101  
     102    return count;
     103  }
     104  
     105  nis_name *
     106  nis_getnames (const_nis_name name)
     107  {
     108    const char *local_domain = nis_local_directory ();
     109    size_t local_domain_len = strlen (local_domain);
     110    size_t name_len = strlen (name);
     111    char *path;
     112    int pos = 0;
     113    char *saveptr = NULL;
     114    int have_point;
     115    const char *cp;
     116    const char *cp2;
     117  
     118    int count = 2;
     119    nis_name *getnames = malloc ((count + 1) * sizeof (char *));
     120    if (__glibc_unlikely (getnames == NULL))
     121        return NULL;
     122  
     123    /* Do we have a fully qualified NIS+ name ? If yes, give it back */
     124    if (name[name_len - 1] == '.')
     125      {
     126        if ((getnames[0] = strdup (name)) == NULL)
     127  	{
     128  	free_null:
     129  	  while (pos-- > 0)
     130  	    free (getnames[pos]);
     131  	  free (getnames);
     132  	  return NULL;
     133  	}
     134  
     135        getnames[1] = NULL;
     136  
     137        return getnames;
     138      }
     139  
     140    /* If the passed NAME is shared a suffix (the latter of course with
     141       a final dot) with each other we pass back NAME with a final
     142       dot.  */
     143    if (local_domain_len > 2)
     144      {
     145        have_point = 0;
     146        cp = &local_domain[local_domain_len - 2];
     147        cp2 = &name[name_len - 1];
     148  
     149        while (*cp == *cp2)
     150  	{
     151  	  if (*cp == '.')
     152  	    have_point = 1;
     153  	  --cp;
     154  	  --cp2;
     155  	  if (cp < local_domain)
     156  	    {
     157  	      have_point = cp2 < name || *cp2 == '.';
     158  	      break;
     159  	    }
     160  	  if (cp2 < name)
     161  	    {
     162  	      have_point = *cp == '.';
     163  	      break;
     164  	    }
     165  	}
     166  
     167        if (have_point)
     168  	{
     169  	  getnames[0] = malloc (name_len + 2);
     170  	  if (getnames[0] == NULL)
     171  	    goto free_null;
     172  
     173  	  strcpy (stpcpy (getnames[0], name), ".");
     174  	  ++pos;
     175  	}
     176      }
     177  
     178    /* Get the search path, where we have to search "name" */
     179    path = getenv ("NIS_PATH");
     180    if (path == NULL)
     181      path = strdupa ("$");
     182    else
     183      path = strdupa (path);
     184  
     185    have_point = strchr (name, '.') != NULL;
     186  
     187    cp = __strtok_r (path, ":", &saveptr);
     188    while (cp)
     189      {
     190        if (strcmp (cp, "$") == 0)
     191  	{
     192  	  const char *cptr = local_domain;
     193  	  char *tmp;
     194  
     195  	  while (*cptr != '\0' && count_dots (cptr) >= 2)
     196  	    {
     197  	      if (pos >= count)
     198  		{
     199  		  count += 5;
     200  		  nis_name *newp = realloc (getnames,
     201  					    (count + 1) * sizeof (char *));
     202  		  if (__glibc_unlikely (newp == NULL))
     203  		    goto free_null;
     204  		  getnames = newp;
     205  		}
     206  	      tmp = malloc (strlen (cptr) + local_domain_len + name_len + 2);
     207  	      if (__glibc_unlikely (tmp == NULL))
     208  		goto free_null;
     209  
     210  	      getnames[pos] = tmp;
     211  	      tmp = stpcpy (tmp, name);
     212  	      *tmp++ = '.';
     213  	      if (cptr[1] != '\0')
     214  		stpcpy (tmp, cptr);
     215  	      else
     216  		++cptr;
     217  
     218  	      ++pos;
     219  
     220  	      while (*cptr != '.' && *cptr != '\0')
     221  		++cptr;
     222  	      if (cptr[0] != '\0' && cptr[1] != '\0')
     223  		/* If we have only ".", don't remove the "." */
     224  		++cptr;
     225  	    }
     226  	}
     227        else
     228  	{
     229  	  char *tmp;
     230  	  size_t cplen = strlen (cp);
     231  
     232  	  if (cp[cplen - 1] == '$')
     233  	    {
     234  	      char *p;
     235  
     236  	      tmp = malloc (cplen + local_domain_len + name_len + 2);
     237  	      if (__glibc_unlikely (tmp == NULL))
     238  		goto free_null;
     239  
     240  	      p = __stpcpy (tmp, name);
     241  	      *p++ = '.';
     242  	      p = __mempcpy (p, cp, cplen);
     243  	      --p;
     244  	      if (p[-1] != '.')
     245  		*p++ = '.';
     246  	      __stpcpy (p, local_domain);
     247  	    }
     248  	  else
     249  	    {
     250  	      char *p;
     251  
     252  	      tmp = malloc (cplen + name_len + 3);
     253  	      if (__glibc_unlikely (tmp == NULL))
     254  		goto free_null;
     255  
     256  	      p = __mempcpy (tmp, name, name_len);
     257  	      *p++ = '.';
     258  	      p = __mempcpy (p, cp, cplen);
     259  	      if (p[-1] != '.')
     260  		*p++ = '.';
     261  	      *p = '\0';
     262  	    }
     263  
     264  	  if (pos >= count)
     265  	    {
     266  	      count += 5;
     267  	      nis_name *newp = realloc (getnames,
     268  					(count + 1) * sizeof (char *));
     269  	      if (__glibc_unlikely (newp == NULL))
     270  		{
     271  		  free (tmp);
     272  		  goto free_null;
     273  		}
     274  	      getnames = newp;
     275  	    }
     276  	  getnames[pos] = tmp;
     277  	  ++pos;
     278  	}
     279        cp = __strtok_r (NULL, ":", &saveptr);
     280      }
     281  
     282    if (pos == 0
     283        && __asprintf (&getnames[pos++], "%s%s%s%s",
     284  		     name, name[name_len - 1] == '.' ? "" : ".",
     285  		     local_domain,
     286  		     local_domain[local_domain_len - 1] == '.' ? "" : ".") < 0)
     287      goto free_null;
     288  
     289    getnames[pos] = NULL;
     290  
     291    return getnames;
     292  }
     293  libnsl_hidden_nolink_def (nis_getnames, GLIBC_2_1)
     294  
     295  void
     296  nis_freenames (nis_name *names)
     297  {
     298    int i = 0;
     299  
     300    while (names[i] != NULL)
     301      {
     302        free (names[i]);
     303        ++i;
     304      }
     305  
     306    free (names);
     307  }
     308  libnsl_hidden_nolink_def  (nis_freenames, GLIBC_2_1)
     309  
     310  name_pos
     311  nis_dir_cmp (const_nis_name n1, const_nis_name n2)
     312  {
     313    int len1, len2;
     314  
     315    len1 = strlen (n1);
     316    len2 = strlen (n2);
     317  
     318    if (len1 == len2)
     319      {
     320        if (strcmp (n1, n2) == 0)
     321  	return SAME_NAME;
     322        else
     323  	return NOT_SEQUENTIAL;
     324      }
     325  
     326    if (len1 < len2)
     327      {
     328        if (n2[len2 - len1 - 1] != '.')
     329  	return NOT_SEQUENTIAL;
     330        else if (strcmp (&n2[len2 - len1], n1) == 0)
     331  	return HIGHER_NAME;
     332        else
     333  	return NOT_SEQUENTIAL;
     334      }
     335    else
     336      {
     337        if (n1[len1 - len2 - 1] != '.')
     338  	return NOT_SEQUENTIAL;
     339        else if (strcmp (&n1[len1 - len2], n2) == 0)
     340  	return LOWER_NAME;
     341        else
     342  	return NOT_SEQUENTIAL;
     343  
     344      }
     345  }
     346  libnsl_hidden_nolink_def (nis_dir_cmp, GLIBC_2_1)
     347  
     348  void
     349  nis_destroy_object (nis_object *obj)
     350  {
     351    nis_free_object (obj);
     352  }
     353  libnsl_hidden_nolink_def (nis_destroy_object, GLIBC_2_1)