From 727b5734187853bd85edb3c5588f4341269302ca Mon Sep 17 00:00:00 2001 From: Susant Sahani Date: Wed, 1 May 2019 04:17:41 +0530 Subject: [PATCH] networkd: Add support for blacklisting servers closes #6260 fuzzer: Add DHCP support for blacklisting servers --- man/systemd.network.xml | 7 ++ src/libsystemd-network/sd-dhcp-client.c | 11 ++- src/libsystemd-network/test-dhcp-client.c | 6 +- src/network/networkd-dhcp4.c | 57 ++++++++++++---- src/network/networkd-network-gperf.gperf | 1 + src/network/networkd-network.c | 67 ++++++++++++++++++- src/network/networkd-network.h | 2 + src/systemd/sd-dhcp-client.h | 3 +- .../fuzz-network-parser/directives.network | 1 + 9 files changed, 136 insertions(+), 19 deletions(-) diff --git a/man/systemd.network.xml b/man/systemd.network.xml index f24bf401595..386c18b25c1 100644 --- a/man/systemd.network.xml +++ b/man/systemd.network.xml @@ -1566,6 +1566,13 @@ + + BlackList= + + A whitespace-separated list of IPv4 addresses. DHCP offers from servers in the list are rejected. + + + diff --git a/src/libsystemd-network/sd-dhcp-client.c b/src/libsystemd-network/sd-dhcp-client.c index 33a8ef79997..84ce8e0da81 100644 --- a/src/libsystemd-network/sd-dhcp-client.c +++ b/src/libsystemd-network/sd-dhcp-client.c @@ -532,7 +532,7 @@ int sd_dhcp_client_set_max_attempts(sd_dhcp_client *client, uint64_t max_attempt int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { assert_return(client, -EINVAL); - if (!IN_SET(client->state, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING)) + if (!IN_SET(client->state, DHCP_STATE_SELECTING, DHCP_STATE_BOUND, DHCP_STATE_RENEWING, DHCP_STATE_REBINDING)) return -EADDRNOTAVAIL; if (ret) @@ -541,11 +541,13 @@ int sd_dhcp_client_get_lease(sd_dhcp_client *client, sd_dhcp_lease **ret) { return 0; } -static void client_notify(sd_dhcp_client *client, int event) { +static int client_notify(sd_dhcp_client *client, int event) { assert(client); if (client->callback) - client->callback(client, event, client->userdata); + return client->callback(client, event, client->userdata); + + return 0; } static int client_initialize(sd_dhcp_client *client) { @@ -1328,6 +1330,9 @@ static int client_handle_offer(sd_dhcp_client *client, DHCPMessage *offer, size_ sd_dhcp_lease_unref(client->lease); client->lease = TAKE_PTR(lease); + if (client_notify(client, SD_DHCP_CLIENT_EVENT_SELECTING) < 0) + return -ENOMSG; + log_dhcp_client(client, "OFFER"); return 0; diff --git a/src/libsystemd-network/test-dhcp-client.c b/src/libsystemd-network/test-dhcp-client.c index 0431e2c3f56..5f31d24d20b 100644 --- a/src/libsystemd-network/test-dhcp-client.c +++ b/src/libsystemd-network/test-dhcp-client.c @@ -418,7 +418,7 @@ static uint8_t test_addr_acq_ack[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, }; -static void test_addr_acq_acquired(sd_dhcp_client *client, int event, +static int test_addr_acq_acquired(sd_dhcp_client *client, int event, void *userdata) { sd_event *e = userdata; sd_dhcp_lease *lease; @@ -426,7 +426,7 @@ static void test_addr_acq_acquired(sd_dhcp_client *client, int event, const struct in_addr *addrs; assert_se(client); - assert_se(event == SD_DHCP_CLIENT_EVENT_IP_ACQUIRE); + assert_se(IN_SET(event, SD_DHCP_CLIENT_EVENT_IP_ACQUIRE, SD_DHCP_CLIENT_EVENT_SELECTING)); assert_se(sd_dhcp_client_get_lease(client, &lease) >= 0); assert_se(lease); @@ -447,6 +447,8 @@ static void test_addr_acq_acquired(sd_dhcp_client *client, int event, printf(" DHCP address acquired\n"); sd_event_exit(e, 0); + + return 0; } static int test_addr_acq_recv_request(size_t size, DHCPMessage *request) { diff --git a/src/network/networkd-dhcp4.c b/src/network/networkd-dhcp4.c index 01f4e955c85..a42eb8686eb 100644 --- a/src/network/networkd-dhcp4.c +++ b/src/network/networkd-dhcp4.c @@ -512,7 +512,35 @@ static int dhcp_lease_acquired(sd_dhcp_client *client, Link *link) { return 0; } -static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { +static int dhcp_server_is_black_listed(Link *link, sd_dhcp_client *client) { + sd_dhcp_lease *lease; + struct in_addr addr; + int r; + + assert(link); + assert(link->network); + assert(client); + + r = sd_dhcp_client_get_lease(client, &lease); + if (r < 0) + return log_link_error_errno(link, r, "Failed to get DHCP lease: %m"); + + r = sd_dhcp_lease_get_server_identifier(lease, &addr); + if (r < 0) + return log_link_debug_errno(link, r, "Failed to get DHCP server ip address: %m"); + + if (set_contains(link->network->dhcp_black_listed_ip, UINT32_TO_PTR(addr.s_addr))) { + log_struct(LOG_DEBUG, + LOG_LINK_INTERFACE(link), + LOG_LINK_MESSAGE(link, "DHCPv4 ip '%u.%u.%u.%u' found in black listed ip addresses, ignoring offer", + ADDRESS_FMT_VAL(addr))); + return true; + } + + return false; +} + +static int dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { Link *link = userdata; int r = 0; @@ -521,7 +549,7 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { assert(link->manager); if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) - return; + return 0; switch (event) { case SD_DHCP_CLIENT_EVENT_STOP: @@ -532,10 +560,8 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { log_link_debug(link, "DHCP client is stopped. Acquiring IPv4 link-local address"); r = sd_ipv4ll_start(link->ipv4ll); - if (r < 0) { - log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); - return; - } + if (r < 0) + return log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); } _fallthrough_; @@ -544,14 +570,14 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { if (link->network->dhcp_critical) { log_link_error(link, "DHCPv4 connection considered system critical, ignoring request to reconfigure it."); - return; + return 0; } if (link->dhcp_lease) { r = dhcp_lease_lost(link); if (r < 0) { link_enter_failed(link); - return; + return r; } } @@ -559,7 +585,7 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { r = dhcp_lease_acquired(client, link); if (r < 0) { link_enter_failed(link); - return; + return r; } } @@ -568,16 +594,23 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { r = dhcp_lease_renew(client, link); if (r < 0) { link_enter_failed(link); - return; + return r; } break; case SD_DHCP_CLIENT_EVENT_IP_ACQUIRE: r = dhcp_lease_acquired(client, link); if (r < 0) { link_enter_failed(link); - return; + return r; } break; + case SD_DHCP_CLIENT_EVENT_SELECTING: + r = dhcp_server_is_black_listed(link, client); + if (r < 0) + return r; + if (r != 0) + return -ENOMSG; + break; default: if (event < 0) log_link_warning_errno(link, event, "DHCP error: Client failed: %m"); @@ -586,7 +619,7 @@ static void dhcp4_handler(sd_dhcp_client *client, int event, void *userdata) { break; } - return; + return 0; } static int dhcp4_set_hostname(Link *link) { diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 98c2241fca2..5a3c99a38f3 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -150,6 +150,7 @@ DHCP.UseTimezone, config_parse_bool, DHCP.IAID, config_parse_iaid, 0, 0 DHCP.ListenPort, config_parse_uint16, 0, offsetof(Network, dhcp_client_port) DHCP.RapidCommit, config_parse_bool, 0, offsetof(Network, rapid_commit) +DHCP.BlackList, config_parse_dhcp_black_listed_ip_address, 0, 0 DHCP.ForceDHCPv6PDOtherInformation, config_parse_bool, 0, offsetof(Network, dhcp6_force_pd_other_information) IPv6AcceptRA.UseAutonomousPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_autonomous_prefix) IPv6AcceptRA.UseOnLinkPrefix, config_parse_bool, 0, offsetof(Network, ipv6_accept_ra_use_onlink_prefix) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 6e0e1eae08d..c02140d93f6 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -512,7 +512,7 @@ static Network *network_free(Network *network) { free(network->dhcp_vendor_class_identifier); strv_free(network->dhcp_user_class); free(network->dhcp_hostname); - + set_free(network->dhcp_black_listed_ip); free(network->mac); strv_free(network->ntp); @@ -1620,6 +1620,71 @@ int config_parse_dhcp_max_attempts( return 0; } +int config_parse_dhcp_black_listed_ip_address( + const char *unit, + const char *filename, + unsigned line, + const char *section, + unsigned section_line, + const char *lvalue, + int ltype, + const char *rvalue, + void *data, + void *userdata) { + + Network *network = data; + const char *p; + int r; + + assert(filename); + assert(lvalue); + assert(rvalue); + assert(data); + + if (isempty(rvalue)) { + network->dhcp_black_listed_ip = set_free(network->dhcp_black_listed_ip); + return 0; + } + + for (p = rvalue;;) { + _cleanup_free_ char *n = NULL; + union in_addr_union ip; + + r = extract_first_word(&p, &n, NULL, 0); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to parse DHCP black listed ip address, ignoring assignment: %s", + rvalue); + return 0; + } + if (r == 0) + return 0; + + r = in_addr_from_string(AF_INET, n, &ip); + if (r < 0) { + log_syntax(unit, LOG_ERR, filename, line, r, + "DHCP black listed ip address is invalid, ignoring assignment: %s", n); + continue; + } + + r = set_ensure_allocated(&network->dhcp_black_listed_ip, NULL); + if (r < 0) + return log_oom(); + + r = set_put(network->dhcp_black_listed_ip, UINT32_TO_PTR(ip.in.s_addr)); + if (r == -EEXIST) { + log_syntax(unit, LOG_WARNING, filename, line, r, + "DHCP black listed ip address is duplicated, ignoring assignment: %s", n); + continue; + } + if (r < 0) + log_syntax(unit, LOG_ERR, filename, line, r, + "Failed to store DHCP black listed ip address '%s', ignoring assignment: %m", n); + } + + return 0; +} + DEFINE_CONFIG_PARSE_ENUM(config_parse_dhcp_use_domains, dhcp_use_domains, DHCPUseDomains, "Failed to parse DHCP use domains setting"); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 9ee8fb72b8d..6ad25045dfc 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -135,6 +135,7 @@ struct Network { bool dhcp_use_hostname; bool dhcp_route_table_set; DHCPUseDomains dhcp_use_domains; + Set *dhcp_black_listed_ip; /* DHCP Server Support */ bool dhcp_server; @@ -305,6 +306,7 @@ CONFIG_PARSER_PROTOTYPE(config_parse_ipv6_privacy_extensions); CONFIG_PARSER_PROTOTYPE(config_parse_hostname); CONFIG_PARSER_PROTOTYPE(config_parse_timezone); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_dns); +CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_black_listed_ip_address); CONFIG_PARSER_PROTOTYPE(config_parse_radv_dns); CONFIG_PARSER_PROTOTYPE(config_parse_radv_search_domains); CONFIG_PARSER_PROTOTYPE(config_parse_dhcp_server_ntp); diff --git a/src/systemd/sd-dhcp-client.h b/src/systemd/sd-dhcp-client.h index d9b57e2c734..5dbfe8e4a12 100644 --- a/src/systemd/sd-dhcp-client.h +++ b/src/systemd/sd-dhcp-client.h @@ -38,6 +38,7 @@ enum { SD_DHCP_CLIENT_EVENT_IP_CHANGE = 2, SD_DHCP_CLIENT_EVENT_EXPIRED = 3, SD_DHCP_CLIENT_EVENT_RENEW = 4, + SD_DHCP_CLIENT_EVENT_SELECTING = 5, }; enum { @@ -98,7 +99,7 @@ enum { typedef struct sd_dhcp_client sd_dhcp_client; -typedef void (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata); +typedef int (*sd_dhcp_client_callback_t)(sd_dhcp_client *client, int event, void *userdata); int sd_dhcp_client_set_callback( sd_dhcp_client *client, sd_dhcp_client_callback_t cb, diff --git a/test/fuzz/fuzz-network-parser/directives.network b/test/fuzz/fuzz-network-parser/directives.network index 9296c16c498..2cf8b6fa049 100644 --- a/test/fuzz/fuzz-network-parser/directives.network +++ b/test/fuzz/fuzz-network-parser/directives.network @@ -59,6 +59,7 @@ ClientIdentifier= ListenPort= UseTimezone= RouteTable= +BlackList= [Route] Destination= Protocol=