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- The Curl Project/curlv5Range: 7.51.0
Patches
31 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
9c91ec778104idn: switch to libidn2 use and IDNA2008 support
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)
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- access.redhat.com/errata/RHSA-2018:2486mitrevendor-advisoryx_refsource_REDHAT
- access.redhat.com/errata/RHSA-2018:3558mitrevendor-advisoryx_refsource_REDHAT
- security.gentoo.org/glsa/201701-47mitrevendor-advisoryx_refsource_GENTOO
- www.securityfocus.com/bid/94107mitrevdb-entryx_refsource_BID
- www.securitytracker.com/id/1037192mitrevdb-entryx_refsource_SECTRACK
- bugzilla.redhat.com/show_bug.cgimitrex_refsource_CONFIRM
- curl.haxx.se/CVE-2016-8625.patchmitrex_refsource_CONFIRM
- curl.haxx.se/docs/adv_20161102K.htmlmitrex_refsource_CONFIRM
- lists.apache.org/thread.html/r58af02e294bd07f487e2c64ffc0a29b837db5600e33b6e698b9d696b%40%3Cissues.bookkeeper.apache.org%3Emitremailing-listx_refsource_MLIST
- lists.apache.org/thread.html/rf4c02775860db415b4955778a131c2795223f61cb8c6a450893651e4%40%3Cissues.bookkeeper.apache.org%3Emitremailing-listx_refsource_MLIST
- www.tenable.com/security/tns-2016-21mitrex_refsource_CONFIRM
News mentions
0No linked articles in our index yet.