From e4336c0a5df42f4c88ed31c4bad743b93b69718f Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Tue, 12 Jan 2021 21:47:23 +0900 Subject: [PATCH] dhcp: length of each user class field must be positive This also fixes an memory leak when sd_dhcp_client_set_user_class() is called multiple times. --- src/libsystemd-network/dhcp-option.c | 10 ++++++---- src/libsystemd-network/sd-dhcp-client.c | 24 ++++++++++++++---------- src/network/networkd-dhcp4.c | 2 +- src/systemd/sd-dhcp-client.h | 2 +- 4 files changed, 22 insertions(+), 16 deletions(-) diff --git a/src/libsystemd-network/dhcp-option.c b/src/libsystemd-network/dhcp-option.c index 70753c68d8..7e3fe4348b 100644 --- a/src/libsystemd-network/dhcp-option.c +++ b/src/libsystemd-network/dhcp-option.c @@ -38,11 +38,14 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, size_t total = 0; char **s; + if (strv_isempty((char **) optval)) + return -EINVAL; + STRV_FOREACH(s, (char **) optval) { size_t len = strlen(*s); - if (len > 255) - return -ENAMETOOLONG; + if (len > 255 || len == 0) + return -EINVAL; total += 1 + len; } @@ -51,14 +54,13 @@ static int option_append(uint8_t options[], size_t size, size_t *offset, return -ENOBUFS; options[*offset] = code; - options[*offset + 1] = total; + options[*offset + 1] = total; *offset += 2; STRV_FOREACH(s, (char **) optval) { size_t len = strlen(*s); options[*offset] = len; - memcpy(&options[*offset + 1], *s, len); *offset += 1 + len; } diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 7604aafd6f..5cf3637d68 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -573,22 +573,26 @@ int sd_dhcp_client_set_mud_url( int sd_dhcp_client_set_user_class( sd_dhcp_client *client, - const char* const* user_class) { + char * const *user_class) { - _cleanup_strv_free_ char **s = NULL; - char **p; + char * const *p; + char **s = NULL; - STRV_FOREACH(p, (char **) user_class) - if (strlen(*p) > 255) - return -ENAMETOOLONG; + assert_return(client, -EINVAL); + assert_return(!strv_isempty(user_class), -EINVAL); - s = strv_copy((char **) user_class); + STRV_FOREACH(p, user_class) { + size_t n = strlen(*p); + + if (n > 255 || n == 0) + return -EINVAL; + } + + s = strv_copy(user_class); if (!s) return -ENOMEM; - client->user_class = TAKE_PTR(s); - - return 0; + return strv_free_and_replace(client->user_class, s); } int sd_dhcp_client_set_client_port( diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 080dcedab5..d5c1c507bf 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -1417,7 +1417,7 @@ int dhcp4_configure(Link *link) { } if (link->network->dhcp_user_class) { - r = sd_dhcp_client_set_user_class(link->dhcp_client, (const char **) link->network->dhcp_user_class); + r = sd_dhcp_client_set_user_class(link->dhcp_client, link->network->dhcp_user_class); if (r < 0) return log_link_warning_errno(link, r, "DHCP4 CLIENT: Failed to set user class: %m"); } diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index c35328a9a6..822286919e 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -181,7 +181,7 @@ int sd_dhcp_client_set_mud_url( const char *mudurl); int sd_dhcp_client_set_user_class( sd_dhcp_client *client, - const char* const *user_class); + char * const *user_class); int sd_dhcp_client_get_lease( sd_dhcp_client *client, sd_dhcp_lease **ret);