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
This commit is contained in:
Thomas Haller
2021-02-10 09:56:08 +01:00
parent 67110d1711
commit 784932550c
3 changed files with 93 additions and 7 deletions

View File

@@ -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);
}
}

View File

@@ -8,8 +8,9 @@
#include <unistd.h>
#include <arpa/inet.h>
#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;
}

View File

@@ -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__ */