]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolved: support libidn2 in addition to libidn
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Wed, 10 May 2017 01:56:34 +0000 (21:56 -0400)
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>
Thu, 11 May 2017 18:25:01 +0000 (14:25 -0400)
libidn2 2.0.0 supports IDNA2008, in contrast to libidn which supports IDNA2003.

https://bugzilla.redhat.com/show_bug.cgi?id=1449145
From that bug report:

Internationalized domain names exist for quite some time (IDNA2003), although
the protocols describing them have evolved in an incompatible way (IDNA2008).
These incompatibilities will prevent applications written for IDNA2003 to
access certain problematic domain names defined with IDNA2008, e.g., faß.de is
translated to domain xn--fa-hia.de with IDNA2008, while in IDNA2003 it is
translated to fass.de domain. That not only causes incompatibility problems,
but may be used as an attack vector to redirect users to different web sites.

v2:
- keep libidn support
- require libidn2 >= 2.0.0
v3:
- keep dns_name_apply_idna caller dumb, and keep the #ifdefs inside of the
  function.
- use both ±IDN and ±IDN2 in the version string

12 files changed:
Makefile.am
README
configure.ac
meson.build
meson_options.txt
src/basic/build.h
src/resolve/resolved-dns-question.c
src/resolve/resolved-manager.c
src/resolve/test-dnssec-complex.c
src/shared/dns-domain.c
src/shared/dns-domain.h
src/test/test-dns-domain.c

index 1284d14e5263f6f316a3e4ae23016396de37996e..e6b573587dd52c7a9f5e62e37f9bb7f7e312e7ab 100644 (file)
@@ -1137,6 +1137,7 @@ libshared_la_CFLAGS = \
        $(AM_CFLAGS) \
        $(ACL_CFLAGS) \
        $(LIBIDN_CFLAGS) \
+       $(LIBIDN2_CFLAGS) \
        $(SECCOMP_CFLAGS) \
        $(BLKID_CFLAGS) \
        $(LIBCRYPTSETUP_CFLAGS)
@@ -1148,6 +1149,7 @@ libshared_la_LIBADD = \
        libudev-internal.la \
        $(ACL_LIBS) \
        $(LIBIDN_LIBS) \
+       $(LIBIDN2_LIBS) \
        $(SECCOMP_LIBS) \
        $(BLKID_LIBS) \
        $(LIBCRYPTSETUP_LIBS)
@@ -1171,6 +1173,7 @@ libsystemd_shared_la_CFLAGS = \
        $(libudev_internal_la_CFLAGS) \
        $(ACL_CFLAGS) \
        $(LIBIDN_CFLAGS) \
+       $(LIBIDN2_CFLAGS) \
        $(SECCOMP_CFLAGS) \
        $(BLKID_CFLAGS) \
        $(LIBCRYPTSETUP_CFLAGS) \
@@ -1185,6 +1188,7 @@ libsystemd_shared_la_LIBADD = \
        $(libudev_internal_la_LIBADD) \
        $(ACL_LIBS) \
        $(LIBIDN_LIBS) \
+       $(LIBIDN2_LIBS) \
        $(SECCOMP_LIBS) \
        $(BLKID_LIBS) \
        $(LIBCRYPTSETUP_LIBS)
diff --git a/README b/README
index d7477510a9c5adec656fe603b47eed4ca4d4739d..427190aa87f775382ae62e71cca8bdbebf2c8279 100644 (file)
--- a/README
+++ b/README
@@ -142,7 +142,7 @@ REQUIREMENTS:
         libqrencode (optional)
         libmicrohttpd (optional)
         libpython (optional)
-        libidn (optional)
+        libidn2 or libidn (optional)
         elfutils >= 158 (optional)
         make, gcc, and similar tools
 
index f59f3faf38459ffab7681873c0895f0c36ef904f..a934fe85c98afefc84f9c93c95d627348aed3514 100644 (file)
@@ -1015,16 +1015,32 @@ AM_CONDITIONAL(HAVE_LIBCURL, [test "$have_libcurl" = "yes"])
 AM_CONDITIONAL(HAVE_REMOTE, [test "$have_microhttpd" = "yes" -o "$have_libcurl" = "yes"])
 
 # ------------------------------------------------------------------------------
+have_libidn2=no
+AC_ARG_ENABLE(libidn2, AS_HELP_STRING([--disable-libidn2], [disable optional LIBIDN2 support]))
+if test "x$enable_libidn2" != "xno"; then
+        PKG_CHECK_MODULES(LIBIDN2, [libidn2 >= 2.0.0],
+               [AC_DEFINE(HAVE_LIBIDN2, 1, [Define if libidn2 is available])
+                have_libidn2=yes
+                M4_DEFINES="$M4_DEFINES -DHAVE_LIBIDN2"],
+               [have_libidn2=no])
+        if test "x$have_libidn2" = "xno" -a "x$enable_libidn2" = "xyes"; then
+                AC_MSG_ERROR([*** libidn2 support requested but libraries not found])
+        fi
+fi
+AM_CONDITIONAL(HAVE_LIBIDN2, [test "$have_libidn2" = "yes"])
+
 have_libidn=no
 AC_ARG_ENABLE(libidn, AS_HELP_STRING([--disable-libidn], [disable optional LIBIDN support]))
-if test "x$enable_libidn" != "xno"; then
-        PKG_CHECK_MODULES(LIBIDN, [libidn],
-               [AC_DEFINE(HAVE_LIBIDN, 1, [Define if libidn is available])
-                have_libidn=yes
-                M4_DEFINES="$M4_DEFINES -DHAVE_LIBIDN"],
-               [have_libidn=no])
-        if test "x$have_libidn" = "xno" -a "x$enable_libidn" = "xyes"; then
-                AC_MSG_ERROR([*** libidn support requested but libraries not found])
+if test "$have_libidn2" != "yes"; then
+        if test "x$enable_libidn" != "xno"; then
+                PKG_CHECK_MODULES(LIBIDN, [libidn],
+                       [AC_DEFINE(HAVE_LIBIDN, 1, [Define if libidn is available])
+                        have_libidn=yes
+                        M4_DEFINES="$M4_DEFINES -DHAVE_LIBIDN"],
+                       [have_libidn=no])
+                if test "x$have_libidn" = "xno" -a "x$enable_libidn" = "xyes"; then
+                        AC_MSG_ERROR([*** libidn support requested but libraries not found])
+                fi
         fi
 fi
 AM_CONDITIONAL(HAVE_LIBIDN, [test "$have_libidn" = "yes"])
@@ -1715,6 +1731,7 @@ AC_MSG_RESULT([
         MICROHTTPD:                        ${have_microhttpd}
         GNUTLS:                            ${have_gnutls}
         libcurl:                           ${have_libcurl}
+        libidn2:                           ${have_libidn2}
         libidn:                            ${have_libidn}
         libiptc:                           ${have_libiptc}
         ELFUTILS:                          ${have_elfutils}
index 14a20530d432a608632f0178c3b83ac548a28f24..2067dfe60467b564daceb56e2eb3f86b44468b2d 100644 (file)
@@ -791,15 +791,29 @@ else
 endif
 
 want_libidn = get_option('libidn')
-if want_libidn != 'false'
+want_libidn2 = get_option('libidn2')
+if want_libidn == 'true' and want_libidn2 == 'true'
+        error('libidn and libidn2 cannot be requested simultaneously')
+endif
+
+if want_libidn2 != 'false' and want_libidn != 'true'
+        libidn = dependency('libidn2',
+                            required : want_libidn2 == 'true')
+        # libidn is used for both libidn and libidn2 objects
+        if libidn.found()
+                conf.set('HAVE_LIBIDN2', true)
+                m4_defines += ['-DHAVE_LIBIDN2']
+        endif
+else
+        libidn = []
+endif
+if not conf.get('HAVE_LIBIDN2', false) and want_libidn != 'false'
         libidn = dependency('libidn',
                             required : want_libidn == 'true')
         if libidn.found()
                 conf.set('HAVE_LIBIDN', true)
                 m4_defines += ['-DHAVE_LIBIDN']
         endif
-else
-        libidn = []
 endif
 
 want_libiptc = get_option('libiptc')
@@ -2428,6 +2442,7 @@ foreach tuple : [
         ['microhttpd'],
         ['gnutls'],
         ['libcurl'],
+        ['libidn2'],
         ['libidn'],
         ['libiptc'],
         ['elfutils'],
index 4e99b25e63fc9b18de2867b9d3f7053956e6ee64..e2e3b7bb4c2fc75ab74b323fe2811aac172c1bba 100644 (file)
@@ -195,6 +195,8 @@ option('libcryptsetup', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'libcryptsetup support')
 option('libcurl', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'libcurl support')
+option('libidn2', type : 'combo', choices : ['auto', 'true', 'false'],
+       description : 'libidn2 support')
 option('libidn', type : 'combo', choices : ['auto', 'true', 'false'],
        description : 'libidn support')
 option('libiptc', type : 'combo', choices : ['auto', 'true', 'false'],
index 91312bd2a3cb89692b781314b6799f22253c3134..3223915da617be3855f891c2168bc91fef4ae97a 100644 (file)
 #define _KMOD_FEATURE_ "-KMOD"
 #endif
 
+#ifdef HAVE_LIBIDN2
+#define _IDN2_FEATURE_ "+IDN2"
+#else
+#define _IDN2_FEATURE_ "-IDN2"
+#endif
+
 #ifdef HAVE_LIBIDN
 #define _IDN_FEATURE_ "+IDN"
 #else
         _BLKID_FEATURE_ " "                                             \
         _ELFUTILS_FEATURE_ " "                                          \
         _KMOD_FEATURE_ " "                                              \
+        _IDN2_FEATURE_ " "                                              \
         _IDN_FEATURE_ " "                                               \
         _CGROUP_HIEARCHY_
index c8b502d1cd7fc22cdf121ab42a3bfb464c7140e5..af29f73164342df23423260df5022ab787e8a844 100644 (file)
@@ -309,8 +309,8 @@ int dns_question_new_address(DnsQuestion **ret, int family, const char *name, bo
                 r = dns_name_apply_idna(name, &buf);
                 if (r < 0)
                         return r;
-
-                name = buf;
+                if (r > 0)
+                        name = buf;
         }
 
         q = dns_question_new(family == AF_UNSPEC ? 2 : 1);
@@ -422,8 +422,8 @@ int dns_question_new_service(
                         r = dns_name_apply_idna(domain, &buf);
                         if (r < 0)
                                 return r;
-
-                        domain = buf;
+                        if (r > 0)
+                                domain = buf;
                 }
 
                 r = dns_service_join(service, type, domain, &joined);
index 9db8b8f61613bd5f2ac366ed30d6dbf0763c913f..cc9b26d4d53d4fca097ee37fbb12f7614515e45c 100644 (file)
 #include <poll.h>
 #include <sys/ioctl.h>
 
+#ifdef HAVE_LIBIDN2
+#include <idn2.h>
+#endif
+
 #include "af-list.h"
 #include "alloc-util.h"
 #include "dirent-util.h"
@@ -324,9 +328,14 @@ static int manager_network_monitor_listen(Manager *m) {
 
 static int determine_hostname(char **full_hostname, char **llmnr_hostname, char **mdns_hostname) {
         _cleanup_free_ char *h = NULL, *n = NULL;
+#if defined(HAVE_LIBIDN2)
+        _cleanup_free_ char *utf8 = NULL;
+#elif defined(HAVE_LIBIDN)
+        int k;
+#endif
         char label[DNS_LABEL_MAX];
-        const char *p;
-        int r, k;
+        const char *p, *decoded;
+        int r;
 
         assert(full_hostname);
         assert(llmnr_hostname);
@@ -339,7 +348,7 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char
                 return log_debug_errno(r, "Can't determine system hostname: %m");
 
         p = h;
-        r = dns_label_unescape(&p, label, sizeof(label));
+        r = dns_label_unescape(&p, label, sizeof label);
         if (r < 0)
                 return log_error_errno(r, "Failed to unescape host name: %m");
         if (r == 0) {
@@ -347,7 +356,16 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char
                 return -EINVAL;
         }
 
-        k = dns_label_undo_idna(label, r, label, sizeof(label));
+#if defined(HAVE_LIBIDN2)
+        r = idn2_to_unicode_8z8z(label, &utf8, 0);
+        if (r != IDN2_OK)
+                return log_error("Failed to undo IDNA: %s", idn2_strerror(r));
+        assert(utf8_is_valid(utf8));
+
+        r = strlen(utf8);
+        decoded = utf8;
+#elif defined(HAVE_LIBIDN)
+        k = dns_label_undo_idna(label, r, label, sizeof label);
         if (k < 0)
                 return log_error_errno(k, "Failed to undo IDNA: %m");
         if (k > 0)
@@ -357,8 +375,12 @@ static int determine_hostname(char **full_hostname, char **llmnr_hostname, char
                 log_error("System hostname is not UTF-8 clean.");
                 return -EINVAL;
         }
+        decoded = label;
+#else
+        decoded = label; /* no decoding */
+#endif
 
-        r = dns_label_escape_new(label, r, &n);
+        r = dns_label_escape_new(decoded, r, &n);
         if (r < 0)
                 return log_error_errno(r, "Failed to escape host name: %m");
 
index 3d7074af11fc841aebbb722e7433a9105a6778c6..090b2fac23bb4dad6551c5a3622b9a597f880821 100644 (file)
@@ -218,7 +218,7 @@ int main(int argc, char* argv[]) {
         test_hostname_lookup(bus, "poettering.de", AF_INET, NULL);
         test_hostname_lookup(bus, "poettering.de", AF_INET6, NULL);
 
-#ifdef HAVE_LIBIDN
+#if defined(HAVE_LIBIDN2) || defined(HAVE_LIBIDN)
         /* Unsigned A with IDNA conversion necessary */
         test_hostname_lookup(bus, "pöttering.de", AF_UNSPEC, NULL);
         test_hostname_lookup(bus, "pöttering.de", AF_INET, NULL);
index 33debadb153ae9f867b60eb8f70c9b4faf2dee2a..40aec3a1ea77cfbc5169b6203d6ddc0607284c29 100644 (file)
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
  ***/
 
-#ifdef HAVE_LIBIDN
-#include <idna.h>
-#include <stringprep.h>
+#if defined(HAVE_LIBIDN2)
+#  include <idn2.h>
+#elif defined(HAVE_LIBIDN)
+#  include <idna.h>
+#  include <stringprep.h>
 #endif
 
 #include <endian.h>
@@ -299,8 +301,8 @@ int dns_label_escape_new(const char *p, size_t l, char **ret) {
         return r;
 }
 
-int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
 #ifdef HAVE_LIBIDN
+int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
         _cleanup_free_ uint32_t *input = NULL;
         size_t input_size, l;
         const char *p;
@@ -348,13 +350,9 @@ int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded
                 decoded[l] = 0;
 
         return (int) l;
-#else
-        return 0;
-#endif
 }
 
 int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max) {
-#ifdef HAVE_LIBIDN
         size_t input_size, output_size;
         _cleanup_free_ uint32_t *input = NULL;
         _cleanup_free_ char *result = NULL;
@@ -399,10 +397,8 @@ int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded,
                 decoded[w] = 0;
 
         return w;
-#else
-        return 0;
-#endif
 }
+#endif
 
 int dns_name_concat(const char *a, const char *b, char **_ret) {
         _cleanup_free_ char *ret = NULL;
@@ -1274,6 +1270,23 @@ int dns_name_common_suffix(const char *a, const char *b, const char **ret) {
 }
 
 int dns_name_apply_idna(const char *name, char **ret) {
+        /* Return negative on error, 0 if not implemented, positive on success. */
+
+#if defined(HAVE_LIBIDN2)
+        int r;
+
+        assert(name);
+        assert(ret);
+
+        r = idn2_lookup_u8((uint8_t*) name, (uint8_t**) ret,
+                           IDN2_NFC_INPUT | IDN2_NONTRANSITIONAL);
+        if (r == IDN2_OK)
+                return 1; /* *ret has been written */
+        else if (IN_SET(r, IDN2_TOO_BIG_DOMAIN, IDN2_TOO_BIG_LABEL))
+                return -ENOSPC;
+        else
+                return -EINVAL;
+#elif defined(HAVE_LIBIDN)
         _cleanup_free_ char *buf = NULL;
         size_t n = 0, allocated = 0;
         bool first = true;
@@ -1323,6 +1336,9 @@ int dns_name_apply_idna(const char *name, char **ret) {
         buf = NULL;
 
         return (int) n;
+#else
+        return 0;
+#endif
 }
 
 int dns_name_is_valid_or_address(const char *name) {
index 03f160369cbb692064bf20bc966756fe6e8c83fb..fca025def01a785c4dae8c7cd7641c7ad2d894aa 100644 (file)
@@ -51,8 +51,10 @@ static inline int dns_name_parent(const char **name) {
         return dns_label_unescape(name, NULL, DNS_LABEL_MAX);
 }
 
+#if defined(HAVE_LIBIDN)
 int dns_label_apply_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
 int dns_label_undo_idna(const char *encoded, size_t encoded_size, char *decoded, size_t decoded_max);
+#endif
 
 int dns_name_concat(const char *a, const char *b, char **ret);
 
index a7cd8e4b5131874d62b5535c1afd6d62941d50d9..d86add94db5daf9a12aa1c5c16a859b3728e0365 100644 (file)
@@ -608,7 +608,7 @@ static void test_dns_name_common_suffix(void) {
 }
 
 static void test_dns_name_apply_idna_one(const char *s, const char *result) {
-#ifdef HAVE_LIBIDN
+#if defined(HAVE_LIBIDN2) || defined(HAVE_LIBIDN)
         _cleanup_free_ char *buf = NULL;
         assert_se(dns_name_apply_idna(s, &buf) >= 0);
         assert_se(dns_name_equal(buf, result) > 0);