VYPR
Unrated severityNVD Advisory· Published Aug 1, 2018· Updated Aug 6, 2024

CVE-2016-8625

CVE-2016-8625

Description

curl before 7.51.0 uses IDNA 2003 for international domain names, potentially resolving to wrong hosts.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

curl before 7.51.0 uses IDNA 2003 for international domain names, potentially resolving to wrong hosts.

## Vulnerability curl versions prior to 7.51.0, when built with libidn, use the outdated IDNA 2003 standard to translate International Domain Names (IDNs) to punycode for DNS resolution [4]. This misalignment with the modern IDNA 2008 standard can cause domains like straße.de to be translated to strasse.de instead of the correct xn--strae-oqa.de [4]. The issue affects all protocols in curl that rely on DNS resolution when libidn is enabled.

Exploitation

An attacker can register a domain that matches the IDNA 2003 translation of a legitimate IDN. For example, if a user intends to visit straße.de, the attacker could control strasse.de and serve malicious content [4]. No authentication or special privileges are required; the attacker only needs to control a domain that is a valid IDNA 2003 translation of a target IDN. The user must be using a vulnerable curl version and the target domain must have an IDN that differs between IDNA 2003 and 2008.

Impact

Successful exploitation leads to the user unknowingly making network requests to a different host than intended [4]. This can result in information disclosure, credential theft, or other attacks depending on the context (e.g., HTTPS connections to the wrong host may still appear valid if the attacker has a certificate for that domain). The impact is limited to scenarios where the IDN translation difference exists.

Mitigation

The fix was released in curl version 7.51.0 [4]. Users should upgrade to curl 7.51.0 or later. Red Hat has provided updated packages in RHSA-2018:3558 (httpd24-curl 7.61.1) [1] and RHSA-2018:2486 (JBoss Core Services) [2]. Tenable LCE 4.8.2 also includes the fix [3]. If upgrading is not possible, consider disabling IDN support or using a different tool for IDN resolution.

AI Insight generated on May 24, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected products

3

Patches

3
3c561c657c2f

THANKS: synced with 7.51.0

https://github.com/curl/curlDaniel StenbergNov 1, 2016via osv
1 file changed · +23 1
  • docs/THANKS+23 1 modified
    @@ -20,6 +20,7 @@ Adriano Meirelles
     Ajit Dhumale
     Aki Koskinen
     Akos Pasztory
    +Akshay Vernekar
     Alain Danteny
     Alan Pinstein
     Albert Chin-A-Young
    @@ -48,6 +49,7 @@ Alexander Krasnostavsky
     Alexander Lazic
     Alexander Pepper
     Alexander Peslyak
    +Alexander Sinditskiy
     Alexander Traud
     Alexander Zhuravlev
     Alexey Borzov
    @@ -77,10 +79,12 @@ Andreas Ntaflos
     Andreas Olsson
     Andreas Rieke
     Andreas Schuldei
    +Andreas Streichardt
     Andreas Wurf
     Andrei Benea
     Andrei Cipu
     Andrei Kurushin
    +Andrei Sedoi
     Andrej E Baranov
     Andrew Benham
     Andrew Biggs
    @@ -215,6 +219,7 @@ Chris Smowton
     Chris Young
     Christian Fillion
     Christian Grothoff
    +Christian Heimes
     Christian Hägele
     Christian Krause
     Christian Kurz
    @@ -291,6 +296,7 @@ Daniel Theron
     Daniel at touchtunes
     Darryl House
     Darshan Mody
    +Darío Hereñú
     Dave Dribin
     Dave Halbakken
     Dave Hamilton
    @@ -496,6 +502,7 @@ Greg Morse
     Greg Onufer
     Greg Pratt
     Greg Zavertnik
    +Gregory Szorc
     Grigory Entin
     Guenole Bescon
     Guenter Knauf
    @@ -817,6 +824,7 @@ Luke Call
     Luke Dashjr
     Luo Jinghua
     Luong Dinh Dung
    +Luật Nguyễn
     Lyndon Hill
     Maciej Karpiuk
     Maciej Puzio
    @@ -869,12 +877,13 @@ Marquis de Muesli
     Martijn Koster
     Martin C. Martin
     Martin Drasar
    +Martin Frodl
     Martin Hager
     Martin Hedenfalk
     Martin Jansen
     Martin Lemke
     Martin Skinner
    -Martin Storsjo
    +Martin Storsjö
     Martin Vejnár
     Marty Kuhrt
     Maruko
    @@ -948,6 +957,7 @@ Mike Power
     Mike Protts
     Mike Revi
     Miklos Nemeth
    +Miloš Ljumović
     Miroslav Franc
     Miroslav Spousta
     Mitz Wark
    @@ -1031,6 +1041,7 @@ Pau Garcia i Quiles
     Paul Donohue
     Paul Harrington
     Paul Howarth
    +Paul Joyce
     Paul Marks
     Paul Marquis
     Paul Moore
    @@ -1101,6 +1112,7 @@ Rafaël Carré
     Rainer Canavan
     Rainer Jung
     Rainer Koenig
    +Rainer Müller
     Rajesh Naganathan
     Rajkumar Mandal
     Ralf S. Engelschall
    @@ -1117,6 +1129,7 @@ Razvan Cojocaru
     Reinhard Max
     Reinout van Schouwen
     Remi Gacogne
    +Remo E
     Renato Botelho
     Renaud Chaillat
     Renaud Duhaut
    @@ -1145,6 +1158,7 @@ Richard Silverman
     Richard van den Berg
     Rick Jones
     Rick Richardson
    +Rider Linden
     Rob Crittenden
     Rob Davies
     Rob Jones
    @@ -1216,9 +1230,11 @@ Scott Cantor
     Scott Davis
     Scott McCreary
     Sean Boudreau
    +Sebastian Mundry
     Sebastian Pohlschmidt
     Sebastian Rasmussen
     Senthil Raja Velu
    +Sergei Kuzmin
     Sergei Nikulov
     Sergey Tatarincev
     Sergio Ballestrero
    @@ -1260,6 +1276,7 @@ Stefan Tomanek
     Stefan Ulrich
     Steinar H. Gunderson
     Stephan Bergmann
    +Stephen Brokenshire
     Stephen Collyer
     Stephen Kick
     Stephen More
    @@ -1330,6 +1347,7 @@ Tobias Stoeckmann
     Toby Peterson
     Todd A Ouska
     Todd Kulesza
    +Todd Short
     Todd Vierling
     Tom Benoist
     Tom Donovan
    @@ -1357,6 +1375,7 @@ Toni Moreno
     Tony Kelman
     Toon Verwaest
     Tor Arntsen
    +Torben Dannhauer
     Torsten Foertsch
     Toshio Kuratomi
     Toshiyuki Maezawa
    @@ -1372,6 +1391,7 @@ Ulf Samuelsson
     Ulrich Doehner
     Ulrich Telle
     Ulrich Zadow
    +Valentin David
     Venkat Akella
     Victor Snezhko
     Vijay Panghal
    @@ -1439,9 +1459,11 @@ dkjjr89 on github
     eXeC64 on github
     jveazey on github
     kreshano on github
    +lukaszgn on github
     marc-groundctl on github
     neex on github
     nk
    +nopjmp on github
     silveja1 on github
     swalkaus at yahoo.com
     tommink[at]post.pl
    
9c91ec778104

idn: switch to libidn2 use and IDNA2008 support

https://github.com/curl/curlDaniel StenbergOct 12, 2016via body-scan
8 files changed · +66 254
  • CMakeLists.txt+3 10 modified
    @@ -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
    
  • configure.ac+29 47 modified
    @@ -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,47 +2918,29 @@ 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"
       ],[
         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"
    -      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"
    +
    +    AC_SUBST([IDN_ENABLED], [1])
    +    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([Cannot find libraries for IDN support: IDN disabled])
    
  • lib/curl_setup.h+3 4 modified
    @@ -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
    
  • lib/easy.c+0 26 modified
    @@ -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;
    
  • lib/strerror.c+2 79 modified
    @@ -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)
     {
    
  • lib/strerror.h+2 2 modified
    @@ -7,7 +7,7 @@
      *                            | (__| |_| |  _ <| |___
      *                             \___|\___/|_| \_\_____|
      *
    - * Copyright (C) 1998 - 2012, Daniel Stenberg, <daniel@haxx.se>, et al.
    + * Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
      *
      * This software is licensed as described in the file COPYING, which
      * you should have received as part of this distribution. The terms
    @@ -26,7 +26,7 @@
     
     const char *Curl_strerror (struct connectdata *conn, int err);
     
    -#ifdef USE_LIBIDN
    +#ifdef USE_LIBIDN2
     const char *Curl_idn_strerror (struct connectdata *conn, int err);
     #endif
     
    
  • lib/url.c+20 79 modified
    @@ -59,24 +59,15 @@
     #include <limits.h>
     #endif
     
    -#ifdef USE_LIBIDN
    -#include <idna.h>
    -#include <tld.h>
    -#include <stringprep.h>
    -#ifdef HAVE_IDN_FREE_H
    -#include <idn-free.h>
    -#else
    -/* prototype from idn-free.h, not provided by libidn 0.4.5's make install! */
    -void idn_free (void *ptr);
    -#endif
    -#ifndef HAVE_IDN_FREE
    -/* if idn_free() was not found in this version of libidn use free() instead */
    -#define idn_free(x) (free)(x)
    -#endif
    +#ifdef USE_LIBIDN2
    +#include <idn2.h>
    +
     #elif defined(USE_WIN32_IDN)
     /* prototype for curl_win32_idn_to_ascii() */
     bool curl_win32_idn_to_ascii(const char *in, char **out);
    -#endif  /* USE_LIBIDN */
    +#endif  /* USE_LIBIDN2 */
    +
    +#include <idn2.h>
     
     #include "urldata.h"
     #include "netrc.h"
    @@ -3771,58 +3762,15 @@ static bool is_ASCII_name(const char *hostname)
       return TRUE;
     }
     
    -#ifdef USE_LIBIDN
    -/*
    - * Check if characters in hostname is allowed in Top Level Domain.
    - */
    -static bool tld_check_name(struct Curl_easy *data,
    -                           const char *ace_hostname)
    -{
    -  size_t err_pos;
    -  char *uc_name = NULL;
    -  int rc;
    -#ifndef CURL_DISABLE_VERBOSE_STRINGS
    -  const char *tld_errmsg = "<no msg>";
    -#else
    -  (void)data;
    -#endif
    -
    -  /* Convert (and downcase) ACE-name back into locale's character set */
    -  rc = idna_to_unicode_lzlz(ace_hostname, &uc_name, 0);
    -  if(rc != IDNA_SUCCESS)
    -    return FALSE;
    -
    -  /* Warning: err_pos receives "the decoded character offset rather than the
    -     byte position in the string." And as of libidn 1.32 that character offset
    -     is for UTF-8, even if the passed in string is another locale. */
    -  rc = tld_check_lz(uc_name, &err_pos, NULL);
    -#ifndef CURL_DISABLE_VERBOSE_STRINGS
    -#ifdef HAVE_TLD_STRERROR
    -  if(rc != TLD_SUCCESS)
    -    tld_errmsg = tld_strerror((Tld_rc)rc);
    -#endif
    -  if(rc != TLD_SUCCESS)
    -    infof(data, "WARNING: TLD check for %s failed; %s\n",
    -          uc_name, tld_errmsg);
    -#endif /* CURL_DISABLE_VERBOSE_STRINGS */
    -  if(uc_name)
    -     idn_free(uc_name);
    -  if(rc != TLD_SUCCESS)
    -    return FALSE;
    -
    -  return TRUE;
    -}
    -#endif
    -
     /*
      * Perform any necessary IDN conversion of hostname
      */
    -static void fix_hostname(struct Curl_easy *data,
    -                         struct connectdata *conn, struct hostname *host)
    +static void fix_hostname(struct connectdata *conn, struct hostname *host)
     {
       size_t len;
    +  struct Curl_easy *data = conn->data;
     
    -#ifndef USE_LIBIDN
    +#ifndef USE_LIBIDN2
       (void)data;
       (void)conn;
     #elif defined(CURL_DISABLE_VERBOSE_STRINGS)
    @@ -3840,25 +3788,18 @@ static void fix_hostname(struct Curl_easy *data,
     
       /* Check name for non-ASCII and convert hostname to ACE form if we can */
       if(!is_ASCII_name(host->name)) {
    -#ifdef USE_LIBIDN
    -    if(stringprep_check_version(LIBIDN_REQUIRED_VERSION)) {
    +#ifdef USE_LIBIDN2
    +    if(idn2_check_version(IDN2_VERSION)) {
           char *ace_hostname = NULL;
    -
    -      int rc = idna_to_ascii_lz(host->name, &ace_hostname, 0);
    -      infof(data, "Input domain encoded as `%s'\n",
    -            stringprep_locale_charset());
    -      if(rc == IDNA_SUCCESS) {
    -        /* tld_check_name() displays a warning if the host name contains
    -           "illegal" characters for this TLD */
    -        (void)tld_check_name(data, ace_hostname);
    -
    -        host->encalloc = ace_hostname;
    +      int rc = idn2_lookup_ul((const char *)host->name, &ace_hostname, 0);
    +      if(rc == IDN2_OK) {
    +        host->encalloc = (char *)ace_hostname;
             /* change the name pointer to point to the encoded hostname */
             host->name = host->encalloc;
           }
           else
             infof(data, "Failed to convert %s to ACE; %s\n", host->name,
    -              Curl_idn_strerror(conn, rc));
    +              idn2_strerror(rc));
         }
     #elif defined(USE_WIN32_IDN)
         char *ace_hostname = NULL;
    @@ -3881,9 +3822,9 @@ static void fix_hostname(struct Curl_easy *data,
      */
     static void free_fixed_hostname(struct hostname *host)
     {
    -#if defined(USE_LIBIDN)
    +#if defined(USE_LIBIDN2)
       if(host->encalloc) {
    -    idn_free(host->encalloc); /* must be freed with idn_free() since this was
    +    idn2_free(host->encalloc); /* must be freed with idn2_free() since this was
                                      allocated by libidn */
         host->encalloc = NULL;
       }
    @@ -6046,11 +5987,11 @@ static CURLcode create_conn(struct Curl_easy *data,
       /*************************************************************
        * IDN-fix the hostnames
        *************************************************************/
    -  fix_hostname(data, conn, &conn->host);
    +  fix_hostname(conn, &conn->host);
       if(conn->bits.conn_to_host)
    -    fix_hostname(data, conn, &conn->conn_to_host);
    +    fix_hostname(conn, &conn->conn_to_host);
       if(conn->proxy.name && *conn->proxy.name)
    -    fix_hostname(data, conn, &conn->proxy);
    +    fix_hostname(conn, &conn->proxy);
     
       /*************************************************************
        * Check whether the host and the "connect to host" are equal.
    
  • lib/version.c+7 7 modified
    @@ -36,8 +36,8 @@
     #  include <ares.h>
     #endif
     
    -#ifdef USE_LIBIDN
    -#include <stringprep.h>
    +#ifdef USE_LIBIDN2
    +#include <idn2.h>
     #endif
     
     #ifdef USE_LIBPSL
    @@ -111,9 +111,9 @@ char *curl_version(void)
       left -= len;
       ptr += len;
     #endif
    -#ifdef USE_LIBIDN
    -  if(stringprep_check_version(LIBIDN_REQUIRED_VERSION)) {
    -    len = snprintf(ptr, left, " libidn/%s", stringprep_check_version(NULL));
    +#ifdef USE_LIBIDN2
    +  if(idn2_check_version(IDN2_VERSION)) {
    +    len = snprintf(ptr, left, " libidn2/%s", idn2_check_version(NULL));
         left -= len;
         ptr += len;
       }
    @@ -365,10 +365,10 @@ curl_version_info_data *curl_version_info(CURLversion stamp)
         version_info.ares_num = aresnum;
       }
     #endif
    -#ifdef USE_LIBIDN
    +#ifdef USE_LIBIDN2
       /* This returns a version string if we use the given version or later,
          otherwise it returns NULL */
    -  version_info.libidn = stringprep_check_version(LIBIDN_REQUIRED_VERSION);
    +  version_info.libidn = idn2_check_version(IDN2_VERSION);
       if(version_info.libidn)
         version_info.features |= CURL_VERSION_IDN;
     #elif defined(USE_WIN32_IDN)
    
9631fa740

added libidn awareness

https://github.com/curl/curlDaniel StenbergApr 26, 2004via body-scan
1 file changed · +15 1
  • lib/version.c+15 1 modified
    @@ -33,6 +33,10 @@
     #include <ares_version.h>
     #endif
     
    +#ifdef USE_LIBIDN
    +#include <stringprep.h>
    +#endif
    +
     #ifdef USE_SSLEAY
     static void getssl_version(char *ptr, long *num)
     {
    @@ -95,6 +99,7 @@ char *curl_version(void)
     {
       static char version[200];
       char *ptr;
    +  int len = sizeof(version);
       strcpy(version, LIBCURL_NAME "/" LIBCURL_VERSION );
       ptr=strchr(version, '\0');
     
    @@ -105,6 +110,7 @@ char *curl_version(void)
         ptr=strchr(version, '\0');
       }
     #endif
    +  len -= strlen(version);
     
     #ifdef HAVE_KRB4
       sprintf(ptr, " krb4");
    @@ -127,6 +133,10 @@ char *curl_version(void)
       sprintf(ptr, " c-ares/%s", ares_version(NULL));
       ptr += strlen(ptr);
     #endif
    +#ifdef USE_LIBIDN
    +  sprintf(ptr, " libidn/%s", stringprep_check_version(NULL));
    +  ptr += strlen(ptr);
    +#endif
     
       return version;
     }
    @@ -168,7 +178,7 @@ static const char *protocols[] = {
     };
     
     static curl_version_info_data version_info = {
    -  CURLVERSION_SECOND,
    +  CURLVERSION_NOW,
       LIBCURL_VERSION,
       LIBCURL_VERSION_NUM,
       OS, /* as found by configure or set by hand at build-time */
    @@ -200,6 +210,9 @@ static curl_version_info_data version_info = {
     #endif
     #if defined(ENABLE_64BIT) && (SIZEOF_CURL_OFF_T > 4)
       | CURL_VERSION_LARGEFILE
    +#endif
    +#ifdef USE_LIBIDN
    +  | CURL_VERSION_IDN
     #endif
       ,
       NULL, /* ssl_version */
    @@ -208,6 +221,7 @@ static curl_version_info_data version_info = {
       protocols,
       NULL, /* c-ares version */
       0,    /* c-ares version numerical */
    +  NULL, /* libidn version */
     };
     
     curl_version_info_data *curl_version_info(CURLversion stamp)
    

Vulnerability mechanics

Root cause

"curl used the outdated IDNA 2003 standard (via libidn) for international domain name processing, which can map domain names to different hosts than the modern IDNA 2008 standard."

Attack vector

An attacker can register an internationalized domain name that is processed differently under the IDNA 2003 standard (used by libidn) versus the IDNA 2008 standard. When curl resolves such a hostname, the outdated punycode translation may map the domain to a different IP address than the user intended, causing network requests to be sent to the wrong host [ref_id=1]. The attack requires the victim to use curl to connect to a specially crafted internationalized domain name.

Affected code

The vulnerability is in the IDN (Internationalized Domain Name) handling code in `lib/url.c`, specifically the `fix_hostname()` function which used the outdated IDNA 2003 standard via the libidn library (functions `idna_to_ascii_lz`, `tld_check_name`, etc.). The patch replaces all libidn calls with libidn2 calls (e.g., `idn2_lookup_ul`) and updates the build system in `configure.ac` and `CMakeLists.txt` to link against libidn2 instead of libidn.

What the fix does

The patch [patch_id=2247613] replaces the libidn library (IDNA 2003) with libidn2 (IDNA 2008) throughout the codebase. In `lib/url.c`, the `fix_hostname()` function now calls `idn2_lookup_ul()` instead of `idna_to_ascii_lz()`, and the TLD validation function `tld_check_name()` is removed entirely. The build system (`configure.ac`, `CMakeLists.txt`) is updated to link against `-lidn2` and check for `idn2_lookup_ul`. The `lib/strerror.c` file removes the old `Curl_idn_strerror()` function that handled libidn-specific error codes. This ensures curl uses the modern IDNA 2008 standard for international domain name processing, preventing hostname confusion.

Preconditions

  • configcurl must be built with IDN support using the old libidn library (IDNA 2003)
  • inputAttacker must control or register an internationalized domain name that is interpreted differently under IDNA 2003 vs IDNA 2008
  • inputVictim must use curl to connect to the attacker-controlled internationalized domain name

Generated on May 24, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

11

News mentions

0

No linked articles in our index yet.