From 784932550ce1824505ff9893585ea8192939200a Mon Sep 17 00:00:00 2001 From: Thomas Haller Date: Wed, 10 Feb 2021 09:56:08 +0100 Subject: [PATCH] dhcp/nettools: validate and normalize Host Name Option (12) The hostname is in the end a string, which means it must be in a known, sensible encoding (UTF-8). Previously, we would not ensure the encoding, nor that the hostname was valid. Fix that. Follow what systemd does with lease_parse_domain(). See-also: https://tools.ietf.org/html/rfc2132#section-3.14 --- src/core/dhcp/nm-dhcp-nettools.c | 10 ++-- src/core/dhcp/nm-dhcp-utils.c | 86 +++++++++++++++++++++++++++++++- src/core/dhcp/nm-dhcp-utils.h | 4 ++ 3 files changed, 93 insertions(+), 7 deletions(-) diff --git a/src/core/dhcp/nm-dhcp-nettools.c b/src/core/dhcp/nm-dhcp-nettools.c index aee092ef10..3bcf5a7ca6 100644 --- a/src/core/dhcp/nm-dhcp-nettools.c +++ b/src/core/dhcp/nm-dhcp-nettools.c @@ -921,7 +921,6 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, gs_unref_hashtable GHashTable *options = NULL; guint8 * l_data; gsize l_data_len; - const char * v_str; guint16 v_u16; gboolean v_bool; int r; @@ -960,14 +959,13 @@ lease_to_ip4_config(NMDedupMultiIndex *multi_idx, r = n_dhcp4_client_lease_query(lease, NM_DHCP_OPTION_DHCP4_HOST_NAME, &l_data, &l_data_len); if (r == 0) { - nm_str_buf_reset(&sbuf); - nm_str_buf_append_len(&sbuf, (const char *) l_data, l_data_len); - v_str = nm_str_buf_get_str(&sbuf); - if (!nm_utils_is_localhost(v_str)) { + gs_free char *s = NULL; + + if (nm_dhcp_lease_data_parse_domain(l_data, l_data_len, &s)) { nm_dhcp_option_add_option(options, _nm_dhcp_option_dhcp4_options, NM_DHCP_OPTION_DHCP4_HOST_NAME, - v_str); + s); } } diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c index bf8d5887e4..508c3d0930 100644 --- a/src/core/dhcp/nm-dhcp-utils.c +++ b/src/core/dhcp/nm-dhcp-utils.c @@ -8,8 +8,9 @@ #include #include -#include "nm-glib-aux/nm-dedup-multi.h" #include "nm-std-aux/unaligned.h" +#include "nm-glib-aux/nm-dedup-multi.h" +#include "systemd/nm-sd-utils-shared.h" #include "nm-dhcp-utils.h" #include "nm-utils.h" @@ -866,3 +867,86 @@ nm_dhcp_lease_data_parse_mtu(const guint8 *data, gsize n_data, uint16_t *out_val *out_val = mtu; return TRUE; } + +gboolean +nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_len) +{ + /* WARNING: this function only validates that the string does not contain + * NUL characters (and ignores one trailing NUL). It does not check character + * encoding! */ + + if (n_data > 0) { + if (memchr(data, n_data - 1, '\0')) { + /* we accept one trailing NUL (not more). + * + * https://tools.ietf.org/html/rfc2132#section-2 + * https://github.com/systemd/systemd/issues/1337 */ + return FALSE; + } + + if (data[n_data - 1] == '\0') + n_data--; + } + + NM_SET_OUT(out_new_len, n_data); + return TRUE; +} + +char * +nm_dhcp_lease_data_parse_domain_validate(const char *str) +{ + gs_free char *s = NULL; + + s = nm_sd_dns_name_normalize(str); + if (!s) + return NULL; + + if (nm_str_is_empty(s) || (s[0] == '.' && s[1] == '\0')) { + /* root domains are not allowed. */ + return NULL; + } + + if (nm_utils_is_localhost(s)) + return NULL; + + if (!g_utf8_validate(s, -1, NULL)) { + /* the result must be valid UTF-8. */ + return NULL; + } + + return g_steal_pointer(&s); +} + +gboolean +nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val) +{ + gs_free char *str1_free = NULL; + const char * str1; + gs_free char *s = NULL; + + /* this is mostly the same as systemd's lease_parse_domain(). */ + + if (!nm_dhcp_lease_data_parse_cstr(data, n_data, &n_data)) + return FALSE; + + if (n_data == 0) { + /* empty domains are rejected. See + * https://tools.ietf.org/html/rfc2132#section-3.14 + * https://tools.ietf.org/html/rfc2132#section-3.17 + * + * Its minimum length is 1. + * + * Note that this is *after* we potentially stripped a trailing NUL. + */ + return FALSE; + } + + str1 = nm_strndup_a(300, (char *) data, n_data, &str1_free); + + s = nm_dhcp_lease_data_parse_domain_validate(str1); + if (!s) + return FALSE; + + *out_val = g_steal_pointer(&s); + return TRUE; +} diff --git a/src/core/dhcp/nm-dhcp-utils.h b/src/core/dhcp/nm-dhcp-utils.h index 52e1ea4fb1..379489a74d 100644 --- a/src/core/dhcp/nm-dhcp-utils.h +++ b/src/core/dhcp/nm-dhcp-utils.h @@ -42,7 +42,11 @@ char *nm_dhcp_utils_get_dhcp6_event_id(GHashTable *lease); /*****************************************************************************/ +char *nm_dhcp_lease_data_parse_domain_validate(const char *str); + gboolean nm_dhcp_lease_data_parse_u16(const guint8 *data, gsize n_data, guint16 *out_val); gboolean nm_dhcp_lease_data_parse_mtu(const guint8 *data, gsize n_data, guint16 *out_val); +gboolean nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_len); +gboolean nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val); #endif /* __NETWORKMANAGER_DHCP_UTILS_H__ */