Commit 9c91ec77 authored by Daniel Stenberg's avatar Daniel Stenberg
Browse files

idn: switch to libidn2 use and IDNA2008 support

CVE-2016-8625

Bug: https://curl.haxx.se/docs/adv_20161102K.html
Reported-by: Christian Heimes
parent 42b650b9
Loading
Loading
Loading
Loading
+3 −10
Original line number Diff line number Diff line
@@ -449,7 +449,7 @@ if(NOT CURL_DISABLE_LDAPS)
endif()

# Check for idn
check_library_exists_concat("idn" idna_to_ascii_lz HAVE_LIBIDN)
check_library_exists_concat("idn2" idn2_lookup_ul HAVE_LIBIDN2)

# Check for symbol dlopen (same as HAVE_LIBDL)
check_library_exists("${CURL_LIBS}" dlopen "" HAVE_DLOPEN)
@@ -618,7 +618,7 @@ check_include_file_concat("des.h" HAVE_DES_H)
check_include_file_concat("err.h"            HAVE_ERR_H)
check_include_file_concat("errno.h"          HAVE_ERRNO_H)
check_include_file_concat("fcntl.h"          HAVE_FCNTL_H)
check_include_file_concat("idn-free.h"       HAVE_IDN_FREE_H)
check_include_file_concat("idn2.h"           HAVE_IDN2_H)
check_include_file_concat("ifaddrs.h"        HAVE_IFADDRS_H)
check_include_file_concat("io.h"             HAVE_IO_H)
check_include_file_concat("krb.h"            HAVE_KRB_H)
@@ -648,7 +648,6 @@ check_include_file_concat("stropts.h" HAVE_STROPTS_H)
check_include_file_concat("termio.h"         HAVE_TERMIO_H)
check_include_file_concat("termios.h"        HAVE_TERMIOS_H)
check_include_file_concat("time.h"           HAVE_TIME_H)
check_include_file_concat("tld.h"            HAVE_TLD_H)
check_include_file_concat("unistd.h"         HAVE_UNISTD_H)
check_include_file_concat("utime.h"          HAVE_UTIME_H)
check_include_file_concat("x509.h"           HAVE_X509_H)
@@ -662,9 +661,6 @@ check_include_file_concat("netinet/if_ether.h" HAVE_NETINET_IF_ETHER_H)
check_include_file_concat("stdint.h"        HAVE_STDINT_H)
check_include_file_concat("sockio.h"        HAVE_SOCKIO_H)
check_include_file_concat("sys/utsname.h"   HAVE_SYS_UTSNAME_H)
check_include_file_concat("idna.h"          HAVE_IDNA_H)



check_type_size(size_t  SIZEOF_SIZE_T)
check_type_size(ssize_t  SIZEOF_SSIZE_T)
@@ -811,9 +807,6 @@ check_symbol_exists(pipe "${CURL_INCLUDES}" HAVE_PIPE)
check_symbol_exists(ftruncate      "${CURL_INCLUDES}" HAVE_FTRUNCATE)
check_symbol_exists(getprotobyname "${CURL_INCLUDES}" HAVE_GETPROTOBYNAME)
check_symbol_exists(getrlimit      "${CURL_INCLUDES}" HAVE_GETRLIMIT)
check_symbol_exists(idn_free       "${CURL_INCLUDES}" HAVE_IDN_FREE)
check_symbol_exists(idna_strerror  "${CURL_INCLUDES}" HAVE_IDNA_STRERROR)
check_symbol_exists(tld_strerror   "${CURL_INCLUDES}" HAVE_TLD_STRERROR)
check_symbol_exists(setlocale      "${CURL_INCLUDES}" HAVE_SETLOCALE)
check_symbol_exists(setrlimit      "${CURL_INCLUDES}" HAVE_SETRLIMIT)
check_symbol_exists(fcntl          "${CURL_INCLUDES}" HAVE_FCNTL)
@@ -1078,7 +1071,7 @@ _add_if("IPv6" ENABLE_IPV6)
_add_if("unix-sockets"  USE_UNIX_SOCKETS)
_add_if("libz"          HAVE_LIBZ)
_add_if("AsynchDNS"     USE_ARES OR USE_THREADS_POSIX OR USE_THREADS_WIN32)
_add_if("IDN"           HAVE_LIBIDN)
_add_if("IDN"           HAVE_LIBIDN2)
_add_if("Largefile"     (CURL_SIZEOF_CURL_OFF_T GREATER 4) AND
                        ((SIZEOF_OFF_T GREATER 4) OR USE_WIN32_LARGE_FILES))
# TODO SSP1 (WinSSL) check is missing
+29 −47
Original line number Diff line number Diff line
@@ -157,7 +157,7 @@ curl_tls_srp_msg="no (--enable-tls-srp)"
    curl_res_msg="default (--enable-ares / --enable-threaded-resolver)"
   curl_ipv6_msg="no      (--enable-ipv6)"
curl_unix_sockets_msg="no      (--enable-unix-sockets)"
    curl_idn_msg="no      (--with-{libidn,winidn})"
    curl_idn_msg="no      (--with-{libidn2,winidn})"
 curl_manual_msg="no      (--enable-manual)"
curl_libcurl_msg="enabled (--disable-libcurl-option)"
curl_verbose_msg="enabled (--disable-verbose)"
@@ -2830,15 +2830,15 @@ dnl **********************************************************************
dnl Check for the presence of IDN libraries and headers
dnl **********************************************************************

AC_MSG_CHECKING([whether to build with libidn])
AC_MSG_CHECKING([whether to build with libidn2])
OPT_IDN="default"
AC_ARG_WITH(libidn,
AC_HELP_STRING([--with-libidn=PATH],[Enable libidn usage])
AC_HELP_STRING([--without-libidn],[Disable libidn usage]),
AC_HELP_STRING([--with-libidn2=PATH],[Enable libidn2 usage])
AC_HELP_STRING([--without-libidn2],[Disable libidn2 usage]),
  [OPT_IDN=$withval])
case "$OPT_IDN" in
  no)
    dnl --without-libidn option used
    dnl --without-libidn2 option used
    want_idn="no"
    AC_MSG_RESULT([no])
    ;;
@@ -2849,13 +2849,13 @@ case "$OPT_IDN" in
    AC_MSG_RESULT([(assumed) yes])
    ;;
  yes)
    dnl --with-libidn option used without path
    dnl --with-libidn2 option used without path
    want_idn="yes"
    want_idn_path="default"
    AC_MSG_RESULT([yes])
    ;;
  *)
    dnl --with-libidn option used with path
    dnl --with-libidn2 option used with path
    want_idn="yes"
    want_idn_path="$withval"
    AC_MSG_RESULT([yes ($withval)])
@@ -2872,33 +2872,33 @@ if test "$want_idn" = "yes"; then
  if test "$want_idn_path" != "default"; then
    dnl path has been specified
    IDN_PCDIR="$want_idn_path/lib$libsuff/pkgconfig"
    CURL_CHECK_PKGCONFIG(libidn, [$IDN_PCDIR])
    CURL_CHECK_PKGCONFIG(libidn2, [$IDN_PCDIR])
    if test "$PKGCONFIG" != "no"; then
      IDN_LIBS=`CURL_EXPORT_PCDIR([$IDN_PCDIR]) dnl
        $PKGCONFIG --libs-only-l libidn 2>/dev/null`
        $PKGCONFIG --libs-only-l libidn2 2>/dev/null`
      IDN_LDFLAGS=`CURL_EXPORT_PCDIR([$IDN_PCDIR]) dnl
        $PKGCONFIG --libs-only-L libidn 2>/dev/null`
        $PKGCONFIG --libs-only-L libidn2 2>/dev/null`
      IDN_CPPFLAGS=`CURL_EXPORT_PCDIR([$IDN_PCDIR]) dnl
        $PKGCONFIG --cflags-only-I libidn 2>/dev/null`
        $PKGCONFIG --cflags-only-I libidn2 2>/dev/null`
      IDN_DIR=`echo $IDN_LDFLAGS | $SED -e 's/-L//'`
    else
      dnl pkg-config not available or provides no info
      IDN_LIBS="-lidn"
      IDN_LIBS="-lidn2"
      IDN_LDFLAGS="-L$want_idn_path/lib$libsuff"
      IDN_CPPFLAGS="-I$want_idn_path/include"
      IDN_DIR="$want_idn_path/lib$libsuff"
    fi
  else
    dnl path not specified
    CURL_CHECK_PKGCONFIG(libidn)
    CURL_CHECK_PKGCONFIG(libidn2)
    if test "$PKGCONFIG" != "no"; then
      IDN_LIBS=`$PKGCONFIG --libs-only-l libidn 2>/dev/null`
      IDN_LDFLAGS=`$PKGCONFIG --libs-only-L libidn 2>/dev/null`
      IDN_CPPFLAGS=`$PKGCONFIG --cflags-only-I libidn 2>/dev/null`
      IDN_LIBS=`$PKGCONFIG --libs-only-l libidn2 2>/dev/null`
      IDN_LDFLAGS=`$PKGCONFIG --libs-only-L libidn2 2>/dev/null`
      IDN_CPPFLAGS=`$PKGCONFIG --cflags-only-I libidn2 2>/dev/null`
      IDN_DIR=`echo $IDN_LDFLAGS | $SED -e 's/-L//'`
    else
      dnl pkg-config not available or provides no info
      IDN_LIBS="-lidn"
      IDN_LIBS="-lidn2"
    fi
  fi
  #
@@ -2918,9 +2918,9 @@ if test "$want_idn" = "yes"; then
  LDFLAGS="$IDN_LDFLAGS $LDFLAGS"
  LIBS="$IDN_LIBS $LIBS"
  #
  AC_MSG_CHECKING([if idna_to_ascii_4i can be linked])
  AC_MSG_CHECKING([if idn2_lookup_ul can be linked])
  AC_LINK_IFELSE([
    AC_LANG_FUNC_LINK_TRY([idna_to_ascii_4i])
    AC_LANG_FUNC_LINK_TRY([idn2_lookup_ul])
  ],[
    AC_MSG_RESULT([yes])
    tst_links_libidn="yes"
@@ -2928,38 +2928,20 @@ if test "$want_idn" = "yes"; then
    AC_MSG_RESULT([no])
    tst_links_libidn="no"
  ])
  if test "$tst_links_libidn" = "no"; then
    AC_MSG_CHECKING([if idna_to_ascii_lz can be linked])
    AC_LINK_IFELSE([
      AC_LANG_FUNC_LINK_TRY([idna_to_ascii_lz])
    ],[
      AC_MSG_RESULT([yes])
      tst_links_libidn="yes"
    ],[
      AC_MSG_RESULT([no])
      tst_links_libidn="no"
    ])
  fi
  #
  AC_CHECK_HEADERS( idn2.h )

  if test "$tst_links_libidn" = "yes"; then
    AC_DEFINE(HAVE_LIBIDN, 1, [Define to 1 if you have the `idn' library (-lidn).])
    AC_DEFINE(HAVE_LIBIDN2, 1, [Define to 1 if you have the `idn2' library (-lidn2).])
    dnl different versions of libidn have different setups of these:
    AC_CHECK_FUNCS( idn_free idna_strerror tld_strerror )
    AC_CHECK_HEADERS( idn-free.h tld.h )
    if test "x$ac_cv_header_tld_h" = "xyes"; then

    AC_SUBST([IDN_ENABLED], [1])
      curl_idn_msg="enabled"
    curl_idn_msg="enabled (libidn2)"
    if test -n "$IDN_DIR" -a "x$cross_compiling" != "xyes"; then
      LD_LIBRARY_PATH="$LD_LIBRARY_PATH:$IDN_DIR"
      export LD_LIBRARY_PATH
      AC_MSG_NOTICE([Added $IDN_DIR to LD_LIBRARY_PATH])
    fi
    else
      AC_MSG_WARN([Libraries for IDN support too old: IDN disabled])
      CPPFLAGS="$clean_CPPFLAGS"
      LDFLAGS="$clean_LDFLAGS"
      LIBS="$clean_LIBS"
    fi
  else
    AC_MSG_WARN([Cannot find libraries for IDN support: IDN disabled])
    CPPFLAGS="$clean_CPPFLAGS"
+3 −4
Original line number Diff line number Diff line
@@ -599,10 +599,9 @@ int netware_init(void);
#endif
#endif

#if defined(HAVE_LIBIDN) && defined(HAVE_TLD_H)
/* The lib was present and the tld.h header (which is missing in libidn 0.3.X
   but we only work with libidn 0.4.1 or later) */
#define USE_LIBIDN
#if defined(HAVE_LIBIDN2) && defined(HAVE_IDN2_H)
/* The lib and header are present */
#define USE_LIBIDN2
#endif

#ifndef SIZEOF_TIME_T
+0 −26
Original line number Diff line number Diff line
@@ -144,28 +144,6 @@ static CURLcode win32_init(void)
  return CURLE_OK;
}

#ifdef USE_LIBIDN
/*
 * Initialise use of IDNA library.
 * It falls back to ASCII if $CHARSET isn't defined. This doesn't work for
 * idna_to_ascii_lz().
 */
static void idna_init (void)
{
#ifdef WIN32
  char buf[60];
  UINT cp = GetACP();

  if(!getenv("CHARSET") && cp > 0) {
    snprintf(buf, sizeof(buf), "CHARSET=cp%u", cp);
    putenv(buf);
  }
#else
  /* to do? */
#endif
}
#endif  /* USE_LIBIDN */

/* true globals -- for curl_global_init() and curl_global_cleanup() */
static unsigned int  initialized;
static long          init_flags;
@@ -262,10 +240,6 @@ static CURLcode global_init(long flags, bool memoryfuncs)
  }
#endif

#ifdef USE_LIBIDN
  idna_init();
#endif

  if(Curl_resolver_global_init()) {
    DEBUGF(fprintf(stderr, "Error: resolver_global_init failed\n"));
    return CURLE_FAILED_INIT;
+2 −79
Original line number Diff line number Diff line
@@ -35,8 +35,8 @@

#include <curl/curl.h>

#ifdef USE_LIBIDN
#include <idna.h>
#ifdef USE_LIBIDN2
#include <idn2.h>
#endif

#ifdef USE_WINDOWS_SSPI
@@ -726,83 +726,6 @@ const char *Curl_strerror(struct connectdata *conn, int err)
  return buf;
}

#ifdef USE_LIBIDN
/*
 * Return error-string for libidn status as returned from idna_to_ascii_lz().
 */
const char *Curl_idn_strerror (struct connectdata *conn, int err)
{
#ifdef HAVE_IDNA_STRERROR
  (void)conn;
  return idna_strerror((Idna_rc) err);
#else
  const char *str;
  char *buf;
  size_t max;

  DEBUGASSERT(conn);

  buf = conn->syserr_buf;
  max = sizeof(conn->syserr_buf)-1;
  *buf = '\0';

#ifndef CURL_DISABLE_VERBOSE_STRINGS
  switch ((Idna_rc)err) {
    case IDNA_SUCCESS:
      str = "No error";
      break;
    case IDNA_STRINGPREP_ERROR:
      str = "Error in string preparation";
      break;
    case IDNA_PUNYCODE_ERROR:
      str = "Error in Punycode operation";
      break;
    case IDNA_CONTAINS_NON_LDH:
      str = "Illegal ASCII characters";
      break;
    case IDNA_CONTAINS_MINUS:
      str = "Contains minus";
      break;
    case IDNA_INVALID_LENGTH:
      str = "Invalid output length";
      break;
    case IDNA_NO_ACE_PREFIX:
      str = "No ACE prefix (\"xn--\")";
      break;
    case IDNA_ROUNDTRIP_VERIFY_ERROR:
      str = "Round trip verify error";
      break;
    case IDNA_CONTAINS_ACE_PREFIX:
      str = "Already have ACE prefix (\"xn--\")";
      break;
    case IDNA_ICONV_ERROR:
      str = "Locale conversion failed";
      break;
    case IDNA_MALLOC_ERROR:
      str = "Allocation failed";
      break;
    case IDNA_DLOPEN_ERROR:
      str = "dlopen() error";
      break;
    default:
      snprintf(buf, max, "error %d", err);
      str = NULL;
      break;
  }
#else
  if((Idna_rc)err == IDNA_SUCCESS)
    str = "No error";
  else
    str = "Error";
#endif
  if(str)
    strncpy(buf, str, max);
  buf[max] = '\0';
  return (buf);
#endif
}
#endif  /* USE_LIBIDN */

#ifdef USE_WINDOWS_SSPI
const char *Curl_sspi_strerror (struct connectdata *conn, int err)
{
Loading