hjl / source-git / glibc

Forked from source-git/glibc 3 years ago
Clone

Blame nis/nss_nisplus/nisplus-service.c

Packit 6c4009
/* Copyright (C) 1997-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>, 1997.
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 <atomic.h>
Packit 6c4009
#include <ctype.h>
Packit 6c4009
#include <errno.h>
Packit 6c4009
#include <netdb.h>
Packit 6c4009
#include <nss.h>
Packit 6c4009
#include <string.h>
Packit 6c4009
#include <rpcsvc/nis.h>
Packit 6c4009
#include <libc-lock.h>
Packit 6c4009
Packit 6c4009
#include "nss-nisplus.h"
Packit 6c4009
Packit 6c4009
__libc_lock_define_initialized (static, lock);
Packit 6c4009
Packit 6c4009
static nis_result *result;
Packit 6c4009
static nis_name tablename_val;
Packit 6c4009
static u_long tablename_len;
Packit 6c4009
Packit 6c4009
#define NISENTRYVAL(idx, col, res) \
Packit 6c4009
  (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_val)
Packit 6c4009
Packit 6c4009
#define NISENTRYLEN(idx, col, res) \
Packit 6c4009
    (NIS_RES_OBJECT (res)[idx].EN_data.en_cols.en_cols_val[col].ec_value.ec_value_len)
Packit 6c4009
Packit 6c4009
Packit 6c4009
static int
Packit 6c4009
_nss_nisplus_parse_servent (nis_result *result, struct servent *serv,
Packit 6c4009
			    char *buffer, size_t buflen, int *errnop)
Packit 6c4009
{
Packit 6c4009
  char *first_unused = buffer;
Packit 6c4009
  size_t room_left = buflen;
Packit 6c4009
Packit 6c4009
  if (result == NULL)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  if ((result->status != NIS_SUCCESS && result->status != NIS_S_SUCCESS)
Packit 6c4009
      || __type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ
Packit 6c4009
      || strcmp (NIS_RES_OBJECT (result)->EN_data.en_type, "services_tbl") != 0
Packit 6c4009
      || NIS_RES_OBJECT (result)->EN_data.en_cols.en_cols_len < 4)
Packit 6c4009
    return 0;
Packit 6c4009
Packit 6c4009
  if (NISENTRYLEN (0, 0, result) >= room_left)
Packit 6c4009
    {
Packit 6c4009
    no_more_room:
Packit 6c4009
      *errnop = ERANGE;
Packit 6c4009
      return -1;
Packit 6c4009
    }
Packit 6c4009
  strncpy (first_unused, NISENTRYVAL (0, 0, result),
Packit 6c4009
           NISENTRYLEN (0, 0, result));
Packit 6c4009
  first_unused[NISENTRYLEN (0, 0, result)] = '\0';
Packit 6c4009
  serv->s_name = first_unused;
Packit 6c4009
  size_t len = strlen (first_unused) + 1;
Packit 6c4009
  room_left -= len;
Packit 6c4009
  first_unused += len;
Packit 6c4009
Packit 6c4009
  if (NISENTRYLEN (0, 2, result) >= room_left)
Packit 6c4009
    goto no_more_room;
Packit 6c4009
  strncpy (first_unused, NISENTRYVAL (0, 2, result),
Packit 6c4009
           NISENTRYLEN (0, 2, result));
Packit 6c4009
  first_unused[NISENTRYLEN (0, 2, result)] = '\0';
Packit 6c4009
  serv->s_proto = first_unused;
Packit 6c4009
  len = strlen (first_unused) + 1;
Packit 6c4009
  room_left -= len;
Packit 6c4009
  first_unused += len;
Packit 6c4009
Packit 6c4009
  serv->s_port = htons (atoi (NISENTRYVAL (0, 3, result)));
Packit 6c4009
Packit 6c4009
  /* XXX Rewrite at some point to allocate the array first and then
Packit 6c4009
     copy the strings.  It wasteful to first concatenate the strings
Packit 6c4009
     to just split them again later.  */
Packit 6c4009
  char *line = first_unused;
Packit 6c4009
  for (unsigned int i = 0; i < NIS_RES_NUMOBJ (result); ++i)
Packit 6c4009
    {
Packit 6c4009
      if (strcmp (NISENTRYVAL (i, 1, result), serv->s_name) != 0)
Packit 6c4009
        {
Packit 6c4009
          if (NISENTRYLEN (i, 1, result) + 2 > room_left)
Packit 6c4009
            goto no_more_room;
Packit 6c4009
	  *first_unused++ = ' ';
Packit 6c4009
          first_unused = __stpncpy (first_unused, NISENTRYVAL (i, 1, result),
Packit 6c4009
				    NISENTRYLEN (i, 1, result));
Packit 6c4009
          room_left -= NISENTRYLEN (i, 1, result) + 1;
Packit 6c4009
        }
Packit 6c4009
    }
Packit 6c4009
  *first_unused++ = '\0';
Packit 6c4009
Packit 6c4009
  /* Adjust the pointer so it is aligned for
Packit 6c4009
     storing pointers.  */
Packit 6c4009
  size_t adjust = ((__alignof__ (char *)
Packit 6c4009
		    - (first_unused - (char *) 0) % __alignof__ (char *))
Packit 6c4009
		   % __alignof__ (char *));
Packit 6c4009
  if (room_left < adjust + sizeof (char *))
Packit 6c4009
    goto no_more_room;
Packit 6c4009
  first_unused += adjust;
Packit 6c4009
  room_left -= adjust;
Packit 6c4009
  serv->s_aliases = (char **) first_unused;
Packit 6c4009
Packit 6c4009
  /* For the terminating NULL pointer.  */
Packit 6c4009
  room_left -= (sizeof (char *));
Packit 6c4009
Packit 6c4009
  unsigned int i = 0;
Packit 6c4009
  while (*line != '\0')
Packit 6c4009
    {
Packit 6c4009
      /* Skip leading blanks.  */
Packit 6c4009
      while (isspace (*line))
Packit 6c4009
        ++line;
Packit 6c4009
Packit 6c4009
      if (*line == '\0')
Packit 6c4009
        break;
Packit 6c4009
Packit 6c4009
      if (room_left < sizeof (char *))
Packit 6c4009
        goto no_more_room;
Packit 6c4009
Packit 6c4009
      room_left -= sizeof (char *);
Packit 6c4009
      serv->s_aliases[i++] = line;
Packit 6c4009
Packit 6c4009
      while (*line != '\0' && *line != ' ')
Packit 6c4009
        ++line;
Packit 6c4009
Packit 6c4009
      if (*line == ' ')
Packit 6c4009
	*line++ = '\0';
Packit 6c4009
    }
Packit 6c4009
  serv->s_aliases[i] = NULL;
Packit 6c4009
Packit 6c4009
  return 1;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
static enum nss_status
Packit 6c4009
_nss_create_tablename (int *errnop)
Packit 6c4009
{
Packit 6c4009
  if (tablename_val == NULL)
Packit 6c4009
    {
Packit 6c4009
      const char *local_dir = nis_local_directory ();
Packit 6c4009
      size_t local_dir_len = strlen (local_dir);
Packit 6c4009
      static const char prefix[] = "services.org_dir.";
Packit 6c4009
Packit 6c4009
      char *p = malloc (sizeof (prefix) + local_dir_len);
Packit 6c4009
      if (p == NULL)
Packit 6c4009
	{
Packit 6c4009
	  *errnop = errno;
Packit 6c4009
	  return NSS_STATUS_TRYAGAIN;
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      memcpy (__stpcpy (p, prefix), local_dir, local_dir_len + 1);
Packit 6c4009
Packit 6c4009
      tablename_len = sizeof (prefix) - 1 + local_dir_len;
Packit 6c4009
Packit 6c4009
      atomic_write_barrier ();
Packit 6c4009
Packit 6c4009
      tablename_val = p;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return NSS_STATUS_SUCCESS;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
Packit 6c4009
enum nss_status
Packit 6c4009
_nss_nisplus_setservent (int stayopen)
Packit 6c4009
{
Packit 6c4009
  enum nss_status status = NSS_STATUS_SUCCESS;
Packit 6c4009
  int err;
Packit 6c4009
Packit 6c4009
  __libc_lock_lock (lock);
Packit 6c4009
Packit 6c4009
  if (result != NULL)
Packit 6c4009
    {
Packit 6c4009
      nis_freeresult (result);
Packit 6c4009
      result = NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (tablename_val == NULL)
Packit 6c4009
    status = _nss_create_tablename (&err;;
Packit 6c4009
Packit 6c4009
  __libc_lock_unlock (lock);
Packit 6c4009
Packit 6c4009
  return status;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
enum nss_status
Packit 6c4009
_nss_nisplus_endservent (void)
Packit 6c4009
{
Packit 6c4009
  __libc_lock_lock (lock);
Packit 6c4009
Packit 6c4009
  if (result != NULL)
Packit 6c4009
    {
Packit 6c4009
      nis_freeresult (result);
Packit 6c4009
      result = NULL;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  __libc_lock_unlock (lock);
Packit 6c4009
Packit 6c4009
  return NSS_STATUS_SUCCESS;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
static enum nss_status
Packit 6c4009
internal_nisplus_getservent_r (struct servent *serv, char *buffer,
Packit 6c4009
			       size_t buflen, int *errnop)
Packit 6c4009
{
Packit 6c4009
  int parse_res;
Packit 6c4009
Packit 6c4009
  /* Get the next entry until we found a correct one. */
Packit 6c4009
  do
Packit 6c4009
    {
Packit 6c4009
      nis_result *saved_res;
Packit 6c4009
Packit 6c4009
      if (result == NULL)
Packit 6c4009
	{
Packit 6c4009
	  saved_res = NULL;
Packit 6c4009
          if (tablename_val == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      enum nss_status status = _nss_create_tablename (errnop);
Packit 6c4009
Packit 6c4009
	      if (status != NSS_STATUS_SUCCESS)
Packit 6c4009
		return status;
Packit 6c4009
	    }
Packit 6c4009
Packit 6c4009
	  result = nis_first_entry (tablename_val);
Packit 6c4009
	  if (result == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      *errnop = errno;
Packit 6c4009
	      return NSS_STATUS_TRYAGAIN;
Packit 6c4009
	    }
Packit 6c4009
	  if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
Packit 6c4009
	    return niserr2nss (result->status);
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  saved_res = result;
Packit 6c4009
	  result = nis_next_entry (tablename_val, &result->cookie);
Packit 6c4009
	  if (result == NULL)
Packit 6c4009
	    {
Packit 6c4009
	      *errnop = errno;
Packit 6c4009
	      return NSS_STATUS_TRYAGAIN;
Packit 6c4009
	    }
Packit 6c4009
	  if (niserr2nss (result->status) != NSS_STATUS_SUCCESS)
Packit 6c4009
	    {
Packit 6c4009
	      nis_freeresult (saved_res);
Packit 6c4009
	      return niserr2nss (result->status);
Packit 6c4009
	    }
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      parse_res = _nss_nisplus_parse_servent (result, serv, buffer,
Packit 6c4009
					      buflen, errnop);
Packit 6c4009
      if (__glibc_unlikely (parse_res == -1))
Packit 6c4009
	{
Packit 6c4009
	  nis_freeresult (result);
Packit 6c4009
	  result = saved_res;
Packit 6c4009
	  *errnop = ERANGE;
Packit 6c4009
	  return NSS_STATUS_TRYAGAIN;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  if (saved_res)
Packit 6c4009
	    nis_freeresult (saved_res);
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
  while (!parse_res);
Packit 6c4009
Packit 6c4009
  return NSS_STATUS_SUCCESS;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
enum nss_status
Packit 6c4009
_nss_nisplus_getservent_r (struct servent *result, char *buffer,
Packit 6c4009
			   size_t buflen, int *errnop)
Packit 6c4009
{
Packit 6c4009
  __libc_lock_lock (lock);
Packit 6c4009
Packit 6c4009
  int status = internal_nisplus_getservent_r (result, buffer, buflen, errnop);
Packit 6c4009
Packit 6c4009
  __libc_lock_unlock (lock);
Packit 6c4009
Packit 6c4009
  return status;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
enum nss_status
Packit 6c4009
_nss_nisplus_getservbyname_r (const char *name, const char *protocol,
Packit 6c4009
			      struct servent *serv,
Packit 6c4009
			      char *buffer, size_t buflen, int *errnop)
Packit 6c4009
{
Packit 6c4009
  if (tablename_val == NULL)
Packit 6c4009
    {
Packit 6c4009
      __libc_lock_lock (lock);
Packit 6c4009
Packit 6c4009
      enum nss_status status = _nss_create_tablename (errnop);
Packit 6c4009
Packit 6c4009
      __libc_lock_unlock (lock);
Packit 6c4009
Packit 6c4009
      if (status != NSS_STATUS_SUCCESS)
Packit 6c4009
	return status;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (name == NULL || protocol == NULL)
Packit 6c4009
    {
Packit 6c4009
      *errnop = EINVAL;
Packit 6c4009
      return NSS_STATUS_NOTFOUND;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  size_t protocol_len = strlen (protocol);
Packit 6c4009
  char buf[strlen (name) + protocol_len + 17 + tablename_len];
Packit 6c4009
  int olderr = errno;
Packit 6c4009
Packit 6c4009
  /* Search at first in the alias list, and use the correct name
Packit 6c4009
     for the next search */
Packit 6c4009
  snprintf (buf, sizeof (buf), "[name=%s,proto=%s],%s", name, protocol,
Packit 6c4009
	    tablename_val);
Packit 6c4009
  nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS | USE_DGRAM,
Packit 6c4009
				 NULL, NULL);
Packit 6c4009
Packit 6c4009
  if (result != NULL)
Packit 6c4009
    {
Packit 6c4009
      char *bufptr = buf;
Packit 6c4009
Packit 6c4009
      /* If we did not find it, try it as original name. But if the
Packit 6c4009
	 database is correct, we should find it in the first case, too */
Packit 6c4009
      if ((result->status != NIS_SUCCESS
Packit 6c4009
	   && result->status != NIS_S_SUCCESS)
Packit 6c4009
	  || __type_of (NIS_RES_OBJECT (result)) != NIS_ENTRY_OBJ
Packit 6c4009
	  || strcmp (NIS_RES_OBJECT (result)->EN_data.en_type,
Packit 6c4009
		     "services_tbl") != 0
Packit 6c4009
	  || NIS_RES_OBJECT (result)->EN_data.en_cols.en_cols_len < 4)
Packit 6c4009
	snprintf (buf, sizeof (buf), "[cname=%s,proto=%s],%s", name, protocol,
Packit 6c4009
		  tablename_val);
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  /* We need to allocate a new buffer since there is no
Packit 6c4009
	     guarantee the returned name has a length limit.  */
Packit 6c4009
	  const char *entryval = NISENTRYVAL(0, 0, result);
Packit 6c4009
	  size_t buflen = (strlen (entryval) + protocol_len + 17
Packit 6c4009
			   + tablename_len);
Packit 6c4009
	  bufptr = alloca (buflen);
Packit 6c4009
	  snprintf (bufptr, buflen, "[cname=%s,proto=%s],%s",
Packit 6c4009
		    entryval, protocol, tablename_val);
Packit 6c4009
	}
Packit 6c4009
Packit 6c4009
      nis_freeresult (result);
Packit 6c4009
      result = nis_list (bufptr, FOLLOW_PATH | FOLLOW_LINKS | USE_DGRAM,
Packit 6c4009
			 NULL, NULL);
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (result == NULL)
Packit 6c4009
    {
Packit 6c4009
      *errnop = ENOMEM;
Packit 6c4009
      return NSS_STATUS_TRYAGAIN;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (niserr2nss (result->status) != NSS_STATUS_SUCCESS))
Packit 6c4009
    {
Packit 6c4009
      enum nss_status status = niserr2nss (result->status);
Packit 6c4009
Packit 6c4009
      __set_errno (olderr);
Packit 6c4009
Packit 6c4009
      nis_freeresult (result);
Packit 6c4009
      return status;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  int parse_res = _nss_nisplus_parse_servent (result, serv, buffer, buflen,
Packit 6c4009
					      errnop);
Packit 6c4009
  nis_freeresult (result);
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (parse_res < 1))
Packit 6c4009
    {
Packit 6c4009
      if (parse_res == -1)
Packit 6c4009
	{
Packit 6c4009
	  *errnop = ERANGE;
Packit 6c4009
	  return NSS_STATUS_TRYAGAIN;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  __set_errno (olderr);
Packit 6c4009
	  return NSS_STATUS_NOTFOUND;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return NSS_STATUS_SUCCESS;
Packit 6c4009
}
Packit 6c4009
Packit 6c4009
enum nss_status
Packit 6c4009
_nss_nisplus_getservbyport_r (const int number, const char *protocol,
Packit 6c4009
			      struct servent *serv,
Packit 6c4009
			      char *buffer, size_t buflen, int *errnop)
Packit 6c4009
{
Packit 6c4009
  if (tablename_val == NULL)
Packit 6c4009
    {
Packit 6c4009
      __libc_lock_lock (lock);
Packit 6c4009
Packit 6c4009
      enum nss_status status = _nss_create_tablename (errnop);
Packit 6c4009
Packit 6c4009
      __libc_lock_unlock (lock);
Packit 6c4009
Packit 6c4009
      if (status != NSS_STATUS_SUCCESS)
Packit 6c4009
	return status;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (protocol == NULL)
Packit 6c4009
    {
Packit 6c4009
      *errnop = EINVAL;
Packit 6c4009
      return NSS_STATUS_NOTFOUND;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  char buf[17 + 3 * sizeof (int) + strlen (protocol) + tablename_len];
Packit 6c4009
  int olderr = errno;
Packit 6c4009
Packit 6c4009
  snprintf (buf, sizeof (buf), "[port=%d,proto=%s],%s",
Packit 6c4009
	    number, protocol, tablename_val);
Packit 6c4009
Packit 6c4009
  nis_result *result = nis_list (buf, FOLLOW_PATH | FOLLOW_LINKS | USE_DGRAM,
Packit 6c4009
				 NULL, NULL);
Packit 6c4009
Packit 6c4009
  if (result == NULL)
Packit 6c4009
    {
Packit 6c4009
      *errnop = ENOMEM;
Packit 6c4009
      return NSS_STATUS_TRYAGAIN;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (niserr2nss (result->status) != NSS_STATUS_SUCCESS))
Packit 6c4009
    {
Packit 6c4009
      enum nss_status status = niserr2nss (result->status);
Packit 6c4009
Packit 6c4009
      __set_errno (olderr);
Packit 6c4009
Packit 6c4009
      nis_freeresult (result);
Packit 6c4009
      return status;
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  int parse_res = _nss_nisplus_parse_servent (result, serv, buffer, buflen,
Packit 6c4009
					      errnop);
Packit 6c4009
  nis_freeresult (result);
Packit 6c4009
Packit 6c4009
  if (__glibc_unlikely (parse_res < 1))
Packit 6c4009
    {
Packit 6c4009
      if (parse_res == -1)
Packit 6c4009
	{
Packit 6c4009
	  *errnop = ERANGE;
Packit 6c4009
	  return NSS_STATUS_TRYAGAIN;
Packit 6c4009
	}
Packit 6c4009
      else
Packit 6c4009
	{
Packit 6c4009
	  __set_errno (olderr);
Packit 6c4009
	  return NSS_STATUS_NOTFOUND;
Packit 6c4009
	}
Packit 6c4009
    }
Packit 6c4009
Packit 6c4009
  return NSS_STATUS_SUCCESS;
Packit 6c4009
}