diff options
Diffstat (limited to 'src/core/dhcp/nm-dhcp-utils.c')
| -rw-r--r-- | src/core/dhcp/nm-dhcp-utils.c | 200 |
1 files changed, 146 insertions, 54 deletions
diff --git a/src/core/dhcp/nm-dhcp-utils.c b/src/core/dhcp/nm-dhcp-utils.c index 081e2841..1bffb3c4 100644 --- a/src/core/dhcp/nm-dhcp-utils.c +++ b/src/core/dhcp/nm-dhcp-utils.c @@ -14,6 +14,7 @@ #include "libnm-systemd-shared/nm-sd-utils-shared.h" #include "nm-dhcp-utils.h" +#include "nm-dhcp-options.h" #include "nm-l3-config-data.h" #include "nm-utils.h" #include "nm-config.h" @@ -295,7 +296,7 @@ process_classful_routes(const char *iface, return; if ((NM_PTRARRAY_LEN(searches) % 2) != 0) { - _LOG2I(LOGD_DHCP, iface, " static routes provided, but invalid"); + _LOG2I(LOGD_DHCP4, iface, " static routes provided, but invalid"); return; } @@ -305,11 +306,11 @@ process_classful_routes(const char *iface, guint32 rt_addr, rt_route; if (inet_pton(AF_INET, *s, &rt_addr) <= 0) { - _LOG2W(LOGD_DHCP, iface, "DHCP provided invalid static route address: '%s'", *s); + _LOG2W(LOGD_DHCP4, iface, "DHCP provided invalid static route address: '%s'", *s); continue; } if (inet_pton(AF_INET, *(s + 1), &rt_route) <= 0) { - _LOG2W(LOGD_DHCP, iface, "DHCP provided invalid static route gateway: '%s'", *(s + 1)); + _LOG2W(LOGD_DHCP4, iface, "DHCP provided invalid static route gateway: '%s'", *(s + 1)); continue; } @@ -340,7 +341,7 @@ process_classful_routes(const char *iface, nm_l3_config_data_add_route_4(l3cd, &route); - _LOG2I(LOGD_DHCP, + _LOG2I(LOGD_DHCP4, iface, " static route %s", nm_platform_ip4_route_to_string(&route, sbuf, sizeof(sbuf))); @@ -352,6 +353,7 @@ process_domain_search(int addr_family, const char *iface, const char *str, NML3C { gs_free const char **searches = NULL; gs_free char *unescaped = NULL; + NMLogDomain logd = NM_IS_IPv4(addr_family) ? LOGD_DHCP4 : LOGD_DHCP6; const char **s; char *p; int i; @@ -373,13 +375,13 @@ process_domain_search(int addr_family, const char *iface, const char *str, NML3C } while (*p++); if (strchr(unescaped, '\\')) { - _LOG2W(LOGD_DHCP, iface, " invalid domain search: '%s'", unescaped); + _LOG2W(logd, iface, " invalid domain search: '%s'", unescaped); return; } searches = nm_strsplit_set(unescaped, " "); for (s = searches; searches && *s; s++) { - _LOG2I(LOGD_DHCP, iface, " domain search '%s'", *s); + _LOG2I(logd, iface, " domain search '%s'", *s); nm_l3_config_data_add_search(l3cd, addr_family, *s); } } @@ -414,18 +416,21 @@ nm_dhcp_utils_ip4_config_from_options(NMDedupMultiIndex *multi_idx, str = g_hash_table_lookup(options, "ip_address"); if (!str || !nm_utils_parse_inaddr_bin(AF_INET, str, NULL, &addr)) return NULL; + if (addr == INADDR_ANY) + return NULL; _LOG2I(LOGD_DHCP4, iface, " address %s", str); str = g_hash_table_lookup(options, "subnet_mask"); if (str && (inet_pton(AF_INET, str, &tmp_addr) > 0)) { - plen = nm_utils_ip4_netmask_to_prefix(tmp_addr); + plen = _nm_utils_ip4_netmask_to_prefix(tmp_addr); _LOG2I(LOGD_DHCP4, iface, " plen %d (%s)", plen, str); } else { /* Get default netmask for the IP according to appropriate class. */ plen = _nm_utils_ip4_get_default_prefix(addr); _LOG2I(LOGD_DHCP4, iface, " plen %d (default)", plen); } + nm_platform_ip4_address_set_addr(&address, addr, plen); /* Routes: if the server returns classless static routes, we MUST ignore @@ -588,7 +593,7 @@ nm_dhcp_utils_ip6_prefix_from_options(GHashTable *options) { gs_strfreev char **split_addr = NULL; NMPlatformIP6Address address = { - 0, + 0, }; struct in6_addr tmp_addr; char *str = NULL; @@ -822,26 +827,6 @@ nm_dhcp_utils_get_leasefile_path(int addr_family, return FALSE; } -char * -nm_dhcp_utils_get_dhcp6_event_id(GHashTable *lease) -{ - const char *start; - const char *iaid; - - if (!lease) - return NULL; - - iaid = g_hash_table_lookup(lease, "iaid"); - if (!iaid) - return NULL; - - start = g_hash_table_lookup(lease, "life_starts"); - if (!start) - return NULL; - - return g_strdup_printf("%s|%s", iaid, start); -} - gboolean nm_dhcp_utils_merge_new_dhcp6_lease(const NML3ConfigData *l3cd_old, const NML3ConfigData *l3cd_new, @@ -855,8 +840,7 @@ nm_dhcp_utils_merge_new_dhcp6_lease(const NML3ConfigData *l3cd_old, const char *start; const char *iaid; - nm_assert(out_l3cd_merged); - nm_assert(!*out_l3cd_merged); + nm_assert(out_l3cd_merged && !*out_l3cd_merged); if (!l3cd_old) return FALSE; @@ -903,28 +887,75 @@ nm_dhcp_utils_merge_new_dhcp6_lease(const NML3ConfigData *l3cd_old, /*****************************************************************************/ +void +nm_dhcp_lease_log_invalid_option(const char *iface, + int addr_family, + guint option, + const char *fmt, + ...) +{ + const char *option_name; + gs_free char *msg = NULL; + va_list ap; + + option_name = nm_dhcp_option_request_string(addr_family, option); + + va_start(ap, fmt); + msg = g_strdup_vprintf(fmt, ap); + va_end(ap); + + _LOG2I(NM_IS_IPv4(addr_family) ? LOGD_DHCP4 : LOGD_DHCP6, + iface, + "error parsing DHCP option %d (%s)%s%s", + option, + option_name, + msg ? ": " : "", + msg ?: ""); +} + gboolean -nm_dhcp_lease_data_parse_u16(const guint8 *data, gsize n_data, uint16_t *out_val) +nm_dhcp_lease_data_parse_u16(const guint8 *data, + gsize n_data, + uint16_t *out_val, + const char *iface, + int addr_family, + guint option) { - if (n_data != 2) + if (n_data != 2) { + nm_dhcp_lease_log_invalid_option(iface, + addr_family, + option, + "invalid option length %lu", + (unsigned long) n_data); return FALSE; + } *out_val = unaligned_read_be16(data); return TRUE; } gboolean -nm_dhcp_lease_data_parse_mtu(const guint8 *data, gsize n_data, uint16_t *out_val) +nm_dhcp_lease_data_parse_mtu(const guint8 *data, + gsize n_data, + uint16_t *out_val, + const char *iface, + int addr_family, + guint option) { uint16_t mtu; - if (!nm_dhcp_lease_data_parse_u16(data, n_data, &mtu)) + if (!nm_dhcp_lease_data_parse_u16(data, n_data, &mtu, iface, addr_family, option)) return FALSE; if (mtu < 68) { /* https://tools.ietf.org/html/rfc2132#section-5.1: * * The minimum legal value for the MTU is 68. */ + nm_dhcp_lease_log_invalid_option(iface, + addr_family, + option, + "value %u is smaller than 68", + mtu); return FALSE; } @@ -933,7 +964,12 @@ nm_dhcp_lease_data_parse_mtu(const guint8 *data, gsize n_data, uint16_t *out_val } gboolean -nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_len) +nm_dhcp_lease_data_parse_cstr(const guint8 *data, + gsize n_data, + gsize *out_new_len, + const char *iface, + int addr_family, + guint option) { /* WARNING: this function only validates that the string does not contain * NUL characters (and ignores trailing NULs). It does not check character @@ -948,6 +984,10 @@ nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_l * * https://tools.ietf.org/html/rfc2132#section-2 * https://github.com/systemd/systemd/issues/1337 */ + nm_dhcp_lease_log_invalid_option(iface, + addr_family, + option, + "string contains embedded NUL"); return FALSE; } } @@ -957,32 +997,47 @@ nm_dhcp_lease_data_parse_cstr(const guint8 *data, gsize n_data, gsize *out_new_l } char * -nm_dhcp_lease_data_parse_domain_validate(const char *str) +nm_dhcp_lease_data_parse_domain_validate(const char *str, + const char *iface, + int addr_family, + guint option) { gs_free char *s = NULL; s = nm_sd_dns_name_normalize(str); if (!s) - return NULL; + goto err; if (nm_str_is_empty(s) || (s[0] == '.' && s[1] == '\0')) { /* root domains are not allowed. */ - return NULL; + goto err; } if (nm_utils_is_localhost(s)) - return NULL; + goto err; if (!g_utf8_validate(s, -1, NULL)) { /* the result must be valid UTF-8. */ - return NULL; + goto err; } return g_steal_pointer(&s); +err: + nm_dhcp_lease_log_invalid_option(iface, + addr_family, + option, + "'%s' is not a valid DNS domain", + str); + return NULL; } gboolean -nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val) +nm_dhcp_lease_data_parse_domain(const guint8 *data, + gsize n_data, + char **out_val, + const char *iface, + int addr_family, + guint option) { gs_free char *str1_free = NULL; const char *str1; @@ -990,7 +1045,7 @@ nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val /* this is mostly the same as systemd's lease_parse_domain(). */ - if (!nm_dhcp_lease_data_parse_cstr(data, n_data, &n_data)) + if (!nm_dhcp_lease_data_parse_cstr(data, n_data, &n_data, iface, addr_family, option)) return FALSE; if (n_data == 0) { @@ -1002,12 +1057,13 @@ nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val * * Note that this is *after* we potentially stripped trailing NULs. */ + nm_dhcp_lease_log_invalid_option(iface, addr_family, option, "empty value"); return FALSE; } str1 = nm_strndup_a(300, (char *) data, n_data, &str1_free); - s = nm_dhcp_lease_data_parse_domain_validate(str1); + s = nm_dhcp_lease_data_parse_domain_validate(str1, iface, addr_family, option); if (!s) return FALSE; @@ -1016,7 +1072,11 @@ nm_dhcp_lease_data_parse_domain(const guint8 *data, gsize n_data, char **out_val } gboolean -nm_dhcp_lease_data_parse_in_addr(const guint8 *data, gsize n_data, in_addr_t *out_val) +nm_dhcp_lease_data_parse_in_addr(const guint8 *data, + gsize n_data, + in_addr_t *out_val, + const char *iface, + guint option) { /* - option 1, https://tools.ietf.org/html/rfc2132#section-3.3 * - option 28, https://tools.ietf.org/html/rfc2132#section-5.3 @@ -1026,8 +1086,14 @@ nm_dhcp_lease_data_parse_in_addr(const guint8 *data, gsize n_data, in_addr_t *ou * according to RFC 3396 section 7. Therefore, it's possible that a * option carrying a IPv4 address has a length > 4. */ - if (n_data < 4) + if (n_data < 4) { + nm_dhcp_lease_log_invalid_option(iface, + AF_INET, + option, + "invalid address length %lu", + (unsigned long) n_data); return FALSE; + } *out_val = unaligned_read_ne32(data); return TRUE; @@ -1070,7 +1136,8 @@ static char * lease_option_print_domain_name(const uint8_t *cache, size_t *n_cachep, const uint8_t **datap, - size_t *n_datap) + size_t *n_datap, + gboolean *invalid) { nm_auto_str_buf NMStrBuf sbuf = NM_STR_BUF_INIT(NM_UTILS_GET_NEXT_REALLOC_SIZE_40, FALSE); const uint8_t *domain; @@ -1081,6 +1148,8 @@ lease_option_print_domain_name(const uint8_t *cache, gboolean first = TRUE; uint8_t c; + NM_SET_OUT(invalid, FALSE); + /* * We are given two adjacent memory regions. The @cache contains alreday parsed * domain names, and the @datap contains the remaining data to parse. @@ -1097,8 +1166,10 @@ lease_option_print_domain_name(const uint8_t *cache, * Note, that each time a jump to an offset is performed, the size of the * cache shrinks, so this is guaranteed to terminate. */ - if (cache + n_cache != *datap) + if (cache + n_cache != *datap) { + NM_SET_OUT(invalid, TRUE); return NULL; + } for (;;) { if (!nm_dhcp_lease_data_consume(domainp, n_domainp, &c, sizeof(c))) @@ -1123,8 +1194,10 @@ lease_option_print_domain_name(const uint8_t *cache, else first = FALSE; - if (!lease_option_print_label(&sbuf, n_label, domainp, n_domainp)) + if (!lease_option_print_label(&sbuf, n_label, domainp, n_domainp)) { + NM_SET_OUT(invalid, TRUE); return NULL; + } break; } @@ -1137,13 +1210,17 @@ lease_option_print_domain_name(const uint8_t *cache, * two high bits are masked out. */ - if (!nm_dhcp_lease_data_consume(domainp, n_domainp, &c, sizeof(c))) + if (!nm_dhcp_lease_data_consume(domainp, n_domainp, &c, sizeof(c))) { + NM_SET_OUT(invalid, TRUE); return NULL; + } offset += c; - if (offset >= n_cache) + if (offset >= n_cache) { + NM_SET_OUT(invalid, TRUE); return NULL; + } domain = cache + offset; n_domain = n_cache - offset; @@ -1155,29 +1232,44 @@ lease_option_print_domain_name(const uint8_t *cache, break; } default: + NM_SET_OUT(invalid, TRUE); return NULL; } } } char ** -nm_dhcp_lease_data_parse_search_list(const guint8 *data, gsize n_data) +nm_dhcp_lease_data_parse_search_list(const guint8 *data, + gsize n_data, + const char *iface, + int addr_family, + guint option) { GPtrArray *array = NULL; const guint8 *cache = data; gsize n_cache = 0; + guint i = 0; for (;;) { gs_free char *s = NULL; - - s = lease_option_print_domain_name(cache, &n_cache, &data, &n_data); - if (!s) + gboolean invalid; + + s = lease_option_print_domain_name(cache, &n_cache, &data, &n_data, &invalid); + if (!s) { + if (iface && invalid) + nm_dhcp_lease_log_invalid_option(iface, + addr_family, + option, + "search domain #%u is invalid", + i); break; + } if (!array) array = g_ptr_array_new(); g_ptr_array_add(array, g_steal_pointer(&s)); + i++; } if (!array) |