aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2017-05-09 21:56:34 -0400
committerZbigniew Jędrzejewski-Szmek <zbyszek@in.waw.pl>2017-05-11 14:25:01 -0400
commit87057e244b4d2e723dc98b2b3cef1901c155f005 (patch)
treec7505157bfa7609a15700082336e4f04311243cd
parent50-udev-default.rules.in: set correct group for mediaX/cecX (#5921) (diff)
downloadsystemd-87057e244b4d2e723dc98b2b3cef1901c155f005.tar.gz
systemd-87057e244b4d2e723dc98b2b3cef1901c155f005.tar.bz2
systemd-87057e244b4d2e723dc98b2b3cef1901c155f005.zip
resolved: support libidn2 in addition to libidn
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
-rw-r--r--Makefile.am4
-rw-r--r--README2
-rw-r--r--configure.ac33
-rw-r--r--meson.build21
-rw-r--r--meson_options.txt2
-rw-r--r--src/basic/build.h7
-rw-r--r--src/resolve/resolved-dns-question.c8
-rw-r--r--src/resolve/resolved-manager.c32
-rw-r--r--src/resolve/test-dnssec-complex.c2
-rw-r--r--src/shared/dns-domain.c38
-rw-r--r--src/shared/dns-domain.h2
-rw-r--r--src/test/test-dns-domain.c2
12 files changed, 119 insertions, 34 deletions
diff --git a/Makefile.am b/Makefile.am
index 1284d14e5..e6b573587 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -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 d7477510a..427190aa8 100644
--- 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
diff --git a/configure.ac b/configure.ac
index f59f3faf3..a934fe85c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -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}
diff --git a/meson.build b/meson.build
index 14a20530d..2067dfe60 100644
--- a/meson.build
+++ b/meson.build
@@ -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'],
diff --git a/meson_options.txt b/meson_options.txt
index 4e99b25e6..e2e3b7bb4 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -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'],
diff --git a/src/basic/build.h b/src/basic/build.h
index 91312bd2a..3223915da 100644
--- a/src/basic/build.h
+++ b/src/basic/build.h
@@ -127,6 +127,12 @@
#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
@@ -154,5 +160,6 @@
_BLKID_FEATURE_ " " \
_ELFUTILS_FEATURE_ " " \
_KMOD_FEATURE_ " " \
+ _IDN2_FEATURE_ " " \
_IDN_FEATURE_ " " \
_CGROUP_HIEARCHY_
diff --git a/src/resolve/resolved-dns-question.c b/src/resolve/resolved-dns-question.c
index c8b502d1c..af29f7316 100644
--- a/src/resolve/resolved-dns-question.c
+++ b/src/resolve/resolved-dns-question.c
@@ -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);
diff --git a/src/resolve/resolved-manager.c b/src/resolve/resolved-manager.c
index 9db8b8f61..cc9b26d4d 100644
--- a/src/resolve/resolved-manager.c
+++ b/src/resolve/resolved-manager.c
@@ -21,6 +21,10 @@
#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");
diff --git a/src/resolve/test-dnssec-complex.c b/src/resolve/test-dnssec-complex.c
index 3d7074af1..090b2fac2 100644
--- a/src/resolve/test-dnssec-complex.c
+++ b/src/resolve/test-dnssec-complex.c
@@ -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);
diff --git a/src/shared/dns-domain.c b/src/shared/dns-domain.c
index 33debadb1..40aec3a1e 100644
--- a/src/shared/dns-domain.c
+++ b/src/shared/dns-domain.c
@@ -17,9 +17,11 @@
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) {
diff --git a/src/shared/dns-domain.h b/src/shared/dns-domain.h
index 03f160369..fca025def 100644
--- a/src/shared/dns-domain.h
+++ b/src/shared/dns-domain.h
@@ -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);
diff --git a/src/test/test-dns-domain.c b/src/test/test-dns-domain.c
index a7cd8e4b5..d86add94d 100644
--- a/src/test/test-dns-domain.c
+++ b/src/test/test-dns-domain.c
@@ -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);