2019-02-04  Florian Weimer  <fweimer@redhat.com>

	[BZ #20018]
	nscd: Do not rely on new GLIBC_PRIVATE ABI after CVE-2016-10739 fix.
	* nscd/nscd-inet_addr.c: New file.  Build resolv/inet_addr.c for
	nscd, without public symbols.
	* nscd/Makefile (nscd-modules): Add it.
	* nscd/gai.c: Include <arpa/inet.h> and change visibility of
	__inet_aton_exact.

2019-01-21  Florian Weimer  <fweimer@redhat.com>

	[BZ #20018]
	CVE-2016-10739
	resolv: Reject trailing characters in host names
	* include/arpa/inet.h (__inet_aton_exact): Declare.
	(inet_aton): Remove hidden prototype.  No longer used internally.
	* nscd/gai.c (__inet_aton): Do not define.
	* nscd/gethstbynm3_r.c (__inet_aton): Likewise.
	* nss/digits_dots.c (__inet_aton): Likewise.
	(__nss_hostname_digits_dots_context): Call __inet_aton_exact.
	* resolv/Versions (GLIBC_PRIVATE): Export __inet_aton_exact from
	libc.
	* resolv/inet_addr.c (inet_aton_end): Remame from __inet_aton.
	Make static.  Add endp parameter.
	(__inet_aton_exact): New function.
	(__inet_aton_ignore_trailing): New function, aliased to inet_aton.
	(__inet_addr): Call inet_aton_end.
	* resolv/res_init.c (res_vinit_1): Truncate nameserver for IPv4,
	not just IPv6.  Call __inet_aton_exact.
	* sysdeps/posix/getaddrinfo.c (gaih_inet): Call __inet_aton_exact.

2019-01-18  Florian Weimer  <fweimer@redhat.com>

	[BZ #24112]
	resolv: Do not send queries for non-host-names in nss_dns.
	* resolv/nss_dns/dns-host.c (check_name): New function.
	(_nss_dns_gethostbyname2_r): Use it.
	(_nss_dns_gethostbyname_r): Likewise.
	(_nss_dns_gethostbyname4_r): Likewise.

Index: glibc-2.11.3/include/arpa/inet.h
===================================================================
--- glibc-2.11.3.orig/include/arpa/inet.h
+++ glibc-2.11.3/include/arpa/inet.h
@@ -1,9 +1,9 @@
 #include <inet/arpa/inet.h>
 
-extern int __inet_aton (__const char *__cp, struct in_addr *__inp);
-libc_hidden_proto (__inet_aton)
+/* Variant of inet_aton which rejects trailing garbage.  */
+extern int __inet_aton_exact (__const char *__cp, struct in_addr *__inp);
+libc_hidden_proto (__inet_aton_exact)
 
-libc_hidden_proto (inet_aton)
 libc_hidden_proto (inet_ntop)
 libc_hidden_proto (inet_pton)
 libc_hidden_proto (inet_makeaddr)
Index: glibc-2.11.3/nscd/Makefile
===================================================================
--- glibc-2.11.3.orig/nscd/Makefile
+++ glibc-2.11.3/nscd/Makefile
@@ -34,7 +34,8 @@ nscd-modules := nscd connections pwdcach
 		getgrnam_r getgrgid_r hstcache gethstbyad_r gethstbynm3_r \
 		getsrvbynm_r getsrvbypt_r servicescache \
 		dbg_log nscd_conf nscd_stat cache mem nscd_setup_thread \
-		xmalloc xstrdup aicache initgrcache gai res_hconf
+		xmalloc xstrdup aicache initgrcache gai res_hconf \
+		nscd-inet_addr
 
 ifeq ($(have-thread-library),yes)
 
@@ -122,6 +123,7 @@ CFLAGS-servicescache.c += $(nscd-cflags)
 CFLAGS-getsrvbynm_r.c += $(nscd-cflags)
 CFLAGS-getsrvbypt_r.c += $(nscd-cflags)
 CFLAGS-res_hconf.c += $(nscd-cflags)
+CFLAGS-nscd-inet_addr.c += $(nscd-cflags)
 
 LDFLAGS = -lselinux
 
Index: glibc-2.11.3/nscd/gai.c
===================================================================
--- glibc-2.11.3.orig/nscd/gai.c
+++ glibc-2.11.3/nscd/gai.c
@@ -21,7 +21,6 @@
 
 /* This file uses the getaddrinfo code but it compiles it without NSCD
    support.  We just need a few symbol renames.  */
-#define __inet_aton inet_aton
 #define __ioctl ioctl
 #define __getsockname getsockname
 #define __socket socket
@@ -33,6 +32,12 @@
 /* nscd uses 1MB or 2MB thread stacks.  */
 #define __libc_use_alloca(size) (size <= __MAX_ALLOCA_CUTOFF)
 
+/* We do not want to export __inet_aton_exact.  Get the prototype and
+   change its visibility to hidden.  */
+#include <arpa/inet.h>
+__typeof__ (__inet_aton_exact) __inet_aton_exact
+  __attribute__ ((visibility ("hidden")));
+
 #include <getaddrinfo.c>
 
 /* Support code.  */
Index: glibc-2.11.3/nscd/gethstbynm3_r.c
===================================================================
--- glibc-2.11.3.orig/nscd/gethstbynm3_r.c
+++ glibc-2.11.3/nscd/gethstbynm3_r.c
@@ -39,8 +39,6 @@
 #define HAVE_LOOKUP_BUFFER	1
 #define HAVE_AF			1
 
-#define __inet_aton inet_aton
-
 #include "../nss/getXXbyYY_r.c"
 
 
Index: glibc-2.11.3/nscd/nscd-inet_addr.c
===================================================================
--- /dev/null
+++ glibc-2.11.3/nscd/nscd-inet_addr.c
@@ -0,0 +1,32 @@
+/* Legacy IPv4 text-to-address functions.  Version for nscd.
+   Copyright (C) 2019 Free Software Foundation, Inc.
+   This file is part of the GNU C Library.
+
+   The GNU C Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   The GNU C Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, see
+   <http://www.gnu.org/licenses/>.  */
+
+#include <arpa/inet.h>
+
+/* We do not want to export __inet_aton_exact.  Get the prototype and
+   change the visibility to hidden.  */
+#include <arpa/inet.h>
+__typeof__ (__inet_aton_exact) __inet_aton_exact
+  __attribute__ ((visibility ("hidden")));
+
+/* Do not provide definitions of the public symbols exported from
+   libc.  */
+#undef weak_alias
+#define weak_alias(from, to)
+
+#include <resolv/inet_addr.c>
Index: glibc-2.11.3/nss/digits_dots.c
===================================================================
--- glibc-2.11.3.orig/nss/digits_dots.c
+++ glibc-2.11.3/nss/digits_dots.c
@@ -29,7 +29,6 @@
 #include "nsswitch.h"
 
 #ifdef USE_NSCD
-# define inet_aton __inet_aton
 # include <nscd/nscd_proto.h>
 #endif
 
@@ -146,7 +145,7 @@ __nss_hostname_digits_dots (const char *
 		     255.255.255.255?  The test below will succeed
 		     spuriously... ???  */
 		  if (af == AF_INET)
-		    ok = __inet_aton (name, (struct in_addr *) host_addr);
+		    ok = __inet_aton_exact (name, (struct in_addr *) host_addr);
 		  else
 		    {
 		      assert (af == AF_INET6);
Index: glibc-2.11.3/resolv/Versions
===================================================================
--- glibc-2.11.3.orig/resolv/Versions
+++ glibc-2.11.3/resolv/Versions
@@ -40,6 +40,7 @@ libc {
 %endif
 
     __res_maybe_init; __res_iclose;
+    __inet_aton_exact;
   }
 }
 
Index: glibc-2.11.3/resolv/inet_addr.c
===================================================================
--- glibc-2.11.3.orig/resolv/inet_addr.c
+++ glibc-2.11.3/resolv/inet_addr.c
@@ -86,27 +86,15 @@ static const char rcsid[] = "$BINDId: in
 #endif
 
 /*
- * Ascii internet address interpretation routine.
- * The value returned is in network order.
- */
-in_addr_t
-inet_addr(const char *cp) {
-	struct in_addr val;
-
-	if (__inet_aton(cp, &val))
-		return (val.s_addr);
-	return (INADDR_NONE);
-}
-
-/*
  * Check whether "cp" is a valid ascii representation
  * of an Internet address and convert to a binary address.
  * Returns 1 if the address is valid, 0 if not.
  * This replaces inet_addr, the return value from which
  * cannot distinguish between failure and a local broadcast address.
+ * Write a pointer to the first non-converted character to *endp.
  */
-int
-__inet_aton(const char *cp, struct in_addr *addr)
+static int
+inet_aton_end(const char *cp, struct in_addr *addr, const char **endp)
 {
 	static const in_addr_t max[4] = { 0xffffffff, 0xffffff, 0xffff, 0xff };
 	in_addr_t val;
@@ -209,6 +197,7 @@ __inet_aton(const char *cp, struct in_ad
 
 	if (addr != NULL)
 		addr->s_addr = res.word | htonl (val);
+	*endp = cp;
 
 #ifdef _LIBC
 	__set_errno (saved_errno);
@@ -221,6 +210,41 @@ ret_0:
 #endif
 	return (0);
 }
-weak_alias (__inet_aton, inet_aton)
-libc_hidden_def (__inet_aton)
-libc_hidden_weak (inet_aton)
+
+int
+__inet_aton_exact (const char *cp, struct in_addr *addr)
+{
+  struct in_addr val;
+  const char *endp;
+  /* Check that inet_aton_end parsed the entire string.  */
+  if (inet_aton_end (cp, &val, &endp) != 0 && *endp == 0)
+    {
+      *addr = val;
+      return 1;
+    }
+  else
+    return 0;
+}
+libc_hidden_def (__inet_aton_exact)
+
+/* inet_aton ignores trailing garbage.  */
+int
+__inet_aton_ignore_trailing (const char *cp, struct in_addr *addr)
+{
+  const char *endp;
+  return  inet_aton_end (cp, addr, &endp);
+}
+weak_alias (__inet_aton_ignore_trailing, inet_aton)
+
+/* ASCII IPv4 Internet address interpretation routine.  The value
+   returned is in network order.  */
+in_addr_t
+__inet_addr (const char *cp)
+{
+  struct in_addr val;
+  const char *endp;
+  if (inet_aton_end (cp, &val, &endp))
+    return val.s_addr;
+  return INADDR_NONE;
+}
+weak_alias (__inet_addr, inet_addr)
Index: glibc-2.11.3/resolv/nss_dns/dns-host.c
===================================================================
--- glibc-2.11.3.orig/resolv/nss_dns/dns-host.c
+++ glibc-2.11.3/resolv/nss_dns/dns-host.c
@@ -249,11 +249,26 @@ _nss_dns_gethostbyname3_r (const char *n
 hidden_def (_nss_dns_gethostbyname3_r)
 
 
+/* Verify that the name looks like a host name.  There is no point in
+   sending a query which will not produce a usable name in the
+   response.  */
+static enum nss_status
+check_name (const char *name, int *h_errnop)
+{
+  if (res_hnok (name))
+    return NSS_STATUS_SUCCESS;
+  *h_errnop = HOST_NOT_FOUND;
+  return NSS_STATUS_NOTFOUND;
+}
+
 enum nss_status
 _nss_dns_gethostbyname2_r (const char *name, int af, struct hostent *result,
 			   char *buffer, size_t buflen, int *errnop,
 			   int *h_errnop)
 {
+  enum nss_status status = check_name (name, h_errnop);
+  if (status != NSS_STATUS_SUCCESS)
+    return status;
   return _nss_dns_gethostbyname3_r (name, af, result, buffer, buflen, errnop,
 				    h_errnop, NULL, NULL);
 }
@@ -264,7 +279,10 @@ _nss_dns_gethostbyname_r (const char *na
 			  char *buffer, size_t buflen, int *errnop,
 			  int *h_errnop)
 {
-  enum nss_status status = NSS_STATUS_NOTFOUND;
+  enum nss_status status = check_name (name, h_errnop);
+  if (status != NSS_STATUS_SUCCESS)
+    return status;
+  status = NSS_STATUS_NOTFOUND;
 
   if (_res.options & RES_USE_INET6)
     status = _nss_dns_gethostbyname3_r (name, AF_INET6, result, buffer,
@@ -282,6 +300,9 @@ _nss_dns_gethostbyname4_r (const char *n
 			   char *buffer, size_t buflen, int *errnop,
 			   int *herrnop, int32_t *ttlp)
 {
+  enum nss_status status = check_name (name, herrnop);
+  if (status != NSS_STATUS_SUCCESS)
+    return status;
   if (__res_maybe_init (&_res, 0) == -1)
     return NSS_STATUS_UNAVAIL;
 
@@ -311,7 +332,6 @@ _nss_dns_gethostbyname4_r (const char *n
   int ans2p_malloced = 0;
 
   int olderr = errno;
-  enum nss_status status;
   int n = __libc_res_nsearch (&_res, name, C_IN, T_QUERY_A_AND_AAAA,
 			      host_buffer.buf->buf, 2048, &host_buffer.ptr,
 			      &ans2p, &nans2p, &resplen2, &ans2p_malloced);
Index: glibc-2.11.3/resolv/res_init.c
===================================================================
--- glibc-2.11.3.orig/resolv/res_init.c
+++ glibc-2.11.3/resolv/res_init.c
@@ -305,8 +305,16 @@ __res_vinit(res_state statp, int preinit
 		    cp = buf + sizeof("nameserver") - 1;
 		    while (*cp == ' ' || *cp == '\t')
 			cp++;
+
+		    /* Ignore trailing contents on the name server line.  */
+		    {
+			char *el;
+			if ((el = strpbrk (cp, " \t\n")) != NULL)
+			    *el = '\0';
+		    }
+
 		    if ((*cp != '\0') && (*cp != '\n')
-			&& __inet_aton(cp, &a)) {
+			&& __inet_aton_exact(cp, &a)) {
 			statp->nsaddr_list[nservall].sin_addr = a;
 			statp->nsaddr_list[nservall].sin_family = AF_INET;
 			statp->nsaddr_list[nservall].sin_port =
@@ -317,9 +325,6 @@ __res_vinit(res_state statp, int preinit
 		    } else {
 			struct in6_addr a6;
 			char *el;
-
-			if ((el = strchr(cp, '\n')) != NULL)
-			    *el = '\0';
 			if ((el = strchr(cp, SCOPE_DELIMITER)) != NULL)
 			    *el = '\0';
 			if ((*cp != '\0') &&
@@ -381,7 +386,7 @@ __res_vinit(res_state statp, int preinit
 				cp++;
 			n = *cp;
 			*cp = 0;
-			if (__inet_aton(net, &a)) {
+			if (__inet_aton_exact(net, &a)) {
 			    statp->sort_list[nsort].addr = a;
 			    if (ISSORTMASK(n)) {
 				*cp++ = n;
@@ -391,7 +396,7 @@ __res_vinit(res_state statp, int preinit
 				    cp++;
 				n = *cp;
 				*cp = 0;
-				if (__inet_aton(net, &a)) {
+				if (__inet_aton_exact(net, &a)) {
 				    statp->sort_list[nsort].mask = a.s_addr;
 				} else {
 				    statp->sort_list[nsort].mask =
Index: glibc-2.11.3/resolv/tst-aton.c
===================================================================
--- glibc-2.11.3.orig/resolv/tst-aton.c
+++ glibc-2.11.3/resolv/tst-aton.c
@@ -15,6 +15,7 @@ static struct tests
   { "-1", 0, 0 },
   { "256", 1, 0x00000100 },
   { "256.", 0, 0 },
+  { "255a", 0, 0 },
   { "256a", 0, 0 },
   { "0x100", 1, 0x00000100 },
   { "0200.0x123456", 1, 0x80123456 },
@@ -39,7 +40,12 @@ static struct tests
   { "1.2.256.4", 0, 0 },
   { "1.2.3.0x100", 0, 0 },
   { "323543357756889", 0, 0 },
-  { "10.1.2.3.4", 0, 0},
+  { "10.1.2.3.4", 0, 0 },
+  { "192.0.2.1", 1, 0xc0000201 },
+  { "192.0.2.2\nX", 1, 0xc0000202 },
+  { "192.0.2.3 Y", 1, 0xc0000203 },
+  { "192.0.2.3Z", 0, 0 },
+  { "192.000.002.010", 1, 0xc0000208 },
 };
 
 
Index: glibc-2.11.3/sysdeps/posix/getaddrinfo.c
===================================================================
--- glibc-2.11.3.orig/sysdeps/posix/getaddrinfo.c
+++ glibc-2.11.3/sysdeps/posix/getaddrinfo.c
@@ -491,7 +491,7 @@ gaih_inet (const char *name, const struc
 	}
 #endif
 
-      if (__inet_aton (name, (struct in_addr *) at->addr) != 0)
+      if (__inet_aton_exact (name, (struct in_addr *) at->addr) != 0)
 	{
 	  if (req->ai_family == AF_UNSPEC || req->ai_family == AF_INET)
 	    at->family = AF_INET;
