Merge pull request #6436 from juga0/features/rfc7844

Features/rfc7844
This commit is contained in:
Lennart Poettering 2017-09-07 10:08:56 +02:00 committed by GitHub
commit 504cef76d4
9 changed files with 219 additions and 26 deletions

View file

@ -997,6 +997,27 @@
Defaults to false.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>Anonymize=</varname></term>
<listitem>
<para>Takes a boolean argument. When true, the options sent to the DHCP server will
follow the <ulink url="https://tools.ietf.org/html/rfc7844">RFC 7844</ulink>
(Anonymity Profiles for DHCP Clients) to minimize disclosure of identifying information.
Defaults to false.</para>
<para>This option should only be set to true when
<varname>MACAddressPolicy=</varname> is set to <literal>random</literal>
(see <citerefentry
project='man-pages'><refentrytitle>systemd.link</refentrytitle><manvolnum>5</manvolnum></citerefentry>).</para>
<para>Note that this configuration will overwrite others.
In concrete, the following variables will be ignored:
<varname>SendHostname=</varname>, <varname>ClientIdentifier=</varname>,
<varname>UseRoutes=</varname>, <varname>SendHostname=</varname>,
<varname>UseMTU=</varname>, <varname>VendorClassIdentifier=</varname>,
<varname>UseTimezone=</varname>.</para>
</listitem>
</varlistentry>
<varlistentry>
<term><varname>SendHostname=</varname></term>
<listitem>

View file

@ -62,6 +62,7 @@ struct sd_dhcp_client {
uint8_t *req_opts;
size_t req_opts_allocated;
size_t req_opts_size;
bool anonymize;
be32_t last_addr;
uint8_t mac_addr[MAX_MAC_ADDR_LEN];
size_t mac_addr_len;
@ -116,6 +117,32 @@ static const uint8_t default_req_opts[] = {
SD_DHCP_OPTION_DOMAIN_NAME_SERVER,
};
/* RFC7844 section 3:
MAY contain the Parameter Request List option.
RFC7844 section 3.6:
The client intending to protect its privacy SHOULD only request a
minimal number of options in the PRL and SHOULD also randomly shuffle
the ordering of option codes in the PRL. If this random ordering
cannot be implemented, the client MAY order the option codes in the
PRL by option code number (lowest to highest).
*/
/* NOTE: using PRL options that Windows 10 RFC7844 implementation uses */
static const uint8_t default_req_opts_anonymize[] = {
SD_DHCP_OPTION_SUBNET_MASK, /* 1 */
SD_DHCP_OPTION_ROUTER, /* 3 */
SD_DHCP_OPTION_DOMAIN_NAME_SERVER, /* 6 */
SD_DHCP_OPTION_DOMAIN_NAME, /* 15 */
SD_DHCP_OPTION_ROUTER_DISCOVER, /* 31 */
SD_DHCP_OPTION_STATIC_ROUTE, /* 33 */
SD_DHCP_OPTION_VENDOR_SPECIFIC, /* 43 */
SD_DHCP_OPTION_NETBIOS_NAMESERVER, /* 44 */
SD_DHCP_OPTION_NETBIOS_NODETYPE, /* 46 */
SD_DHCP_OPTION_NETBIOS_SCOPE, /* 47 */
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE, /* 121 */
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE, /* 249 */
SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY, /* 252 */
};
static int client_receive_message_raw(
sd_event_source *s,
int fd,
@ -588,11 +615,18 @@ static int client_message_init(
it MUST include that list in any subsequent DHCPREQUEST
messages.
*/
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
client->req_opts_size, client->req_opts);
if (r < 0)
return r;
/* RFC7844 section 3:
MAY contain the Parameter Request List option. */
/* NOTE: in case that there would be an option to do not send
* any PRL at all, the size should be checked before sending */
if (client->req_opts_size > 0) {
r = dhcp_option_append(&packet->dhcp, optlen, &optoffset, 0,
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST,
client->req_opts_size, client->req_opts);
if (r < 0)
return r;
}
/* RFC2131 section 3.5:
The client SHOULD include the maximum DHCP message size option to
@ -616,12 +650,16 @@ static int client_message_init(
Maximum DHCP Message Size option is the total maximum packet size,
including IP and UDP headers.)
*/
max_size = htobe16(size);
r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
2, &max_size);
if (r < 0)
return r;
/* RFC7844 section 3:
SHOULD NOT contain any other option. */
if (!client->anonymize) {
max_size = htobe16(size);
r = dhcp_option_append(&packet->dhcp, client->mtu, &optoffset, 0,
SD_DHCP_OPTION_MAXIMUM_MESSAGE_SIZE,
2, &max_size);
if (r < 0)
return r;
}
*_optlen = optlen;
*_optoffset = optoffset;
@ -1783,7 +1821,14 @@ int sd_dhcp_client_start(sd_dhcp_client *client) {
if (r < 0)
return r;
if (client->last_addr)
/* RFC7844 section 3.3:
SHOULD perform a complete four-way handshake, starting with a
DHCPDISCOVER, to obtain a new address lease. If the client can
ascertain that this is exactly the same network to which it was
previously connected, and if the link-layer address did not change,
the client MAY issue a DHCPREQUEST to try to reclaim the current
address. */
if (client->last_addr && !client->anonymize)
client->state = DHCP_STATE_INIT_REBOOT;
r = client_start(client);
@ -1875,7 +1920,7 @@ sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client) {
return mfree(client);
}
int sd_dhcp_client_new(sd_dhcp_client **ret) {
int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize) {
_cleanup_(sd_dhcp_client_unrefp) sd_dhcp_client *client = NULL;
assert_return(ret, -EINVAL);
@ -1892,8 +1937,15 @@ int sd_dhcp_client_new(sd_dhcp_client **ret) {
client->mtu = DHCP_DEFAULT_MIN_SIZE;
client->port = DHCP_PORT_CLIENT;
client->req_opts_size = ELEMENTSOF(default_req_opts);
client->req_opts = memdup(default_req_opts, client->req_opts_size);
client->anonymize = !!anonymize;
/* NOTE: this could be moved to a function. */
if (anonymize) {
client->req_opts_size = ELEMENTSOF(default_req_opts_anonymize);
client->req_opts = memdup(default_req_opts_anonymize, client->req_opts_size);
} else {
client->req_opts_size = ELEMENTSOF(default_req_opts);
client->req_opts = memdup(default_req_opts, client->req_opts_size);
}
if (!client->req_opts)
return -ENOMEM;

View file

@ -56,7 +56,8 @@ static void test_request_basic(sd_event *e) {
if (verbose)
printf("* %s\n", __FUNCTION__);
r = sd_dhcp_client_new(&client);
/* Initialize client without Anonymize settings. */
r = sd_dhcp_client_new(&client, false);
assert_se(r >= 0);
assert_se(client);
@ -78,6 +79,8 @@ static void test_request_basic(sd_event *e) {
SD_DHCP_OPTION_SUBNET_MASK) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
SD_DHCP_OPTION_ROUTER) == -EEXIST);
/* This PRL option is not set when using Anonymize, but in this test
* Anonymize settings are not being used. */
assert_se(sd_dhcp_client_set_request_option(client,
SD_DHCP_OPTION_HOST_NAME) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client,
@ -97,10 +100,49 @@ static void test_request_basic(sd_event *e) {
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST)
== -EINVAL);
assert_se(sd_dhcp_client_set_request_option(client, 33) == 0);
assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client, 44) == 0);
assert_se(sd_dhcp_client_set_request_option(client, 33) == -EEXIST);
/* RFC7844: option 33 (SD_DHCP_OPTION_STATIC_ROUTE) is set in the
* default PRL when using Anonymize, so it is changed to other option
* that is not set by default, to check that it succed setting it.
* Ooptions not set by default (using or not anonymize) are option 17
* (SD_DHCP_OPTION_ROOT_PATH) and 42 (SD_DHCP_OPTION_NTP_SERVER) */
assert_se(sd_dhcp_client_set_request_option(client, 17) == 0);
assert_se(sd_dhcp_client_set_request_option(client, 17) == -EEXIST);
assert_se(sd_dhcp_client_set_request_option(client, 42) == 0);
assert_se(sd_dhcp_client_set_request_option(client, 17) == -EEXIST);
sd_dhcp_client_unref(client);
}
static void test_request_anonymize(sd_event *e) {
int r;
sd_dhcp_client *client;
if (verbose)
printf("* %s\n", __FUNCTION__);
/* Initialize client with Anonymize settings. */
r = sd_dhcp_client_new(&client, true);
assert_se(r >= 0);
assert_se(client);
r = sd_dhcp_client_attach_event(client, e, 0);
assert_se(r >= 0);
assert_se(sd_dhcp_client_set_request_option(client,
SD_DHCP_OPTION_NETBIOS_NAMESERVER) == -EEXIST);
/* This PRL option is not set when using Anonymize */
assert_se(sd_dhcp_client_set_request_option(client,
SD_DHCP_OPTION_HOST_NAME) == 0);
assert_se(sd_dhcp_client_set_request_option(client,
SD_DHCP_OPTION_PARAMETER_REQUEST_LIST)
== -EINVAL);
/* RFC7844: option 101 (SD_DHCP_OPTION_NEW_TZDB_TIMEZONE) is not set in the
* default PRL when using Anonymize, */
assert_se(sd_dhcp_client_set_request_option(client, 101) == 0);
assert_se(sd_dhcp_client_set_request_option(client, 101) == -EEXIST);
sd_dhcp_client_unref(client);
}
@ -236,7 +278,7 @@ static void test_discover_message(sd_event *e) {
if (verbose)
printf("* %s\n", __FUNCTION__);
r = sd_dhcp_client_new(&client);
r = sd_dhcp_client_new(&client, false);
assert_se(r >= 0);
assert_se(client);
@ -451,7 +493,7 @@ static void test_addr_acq(sd_event *e) {
if (verbose)
printf("* %s\n", __FUNCTION__);
r = sd_dhcp_client_new(&client);
r = sd_dhcp_client_new(&client, false);
assert_se(r >= 0);
assert_se(client);
@ -497,6 +539,7 @@ int main(int argc, char *argv[]) {
assert_se(sd_event_new(&e) >= 0);
test_request_basic(e);
test_request_anonymize(e);
test_checksum();
test_discover_message(e);

View file

@ -583,7 +583,7 @@ int dhcp4_configure(Link *link) {
assert(link->network->dhcp & ADDRESS_FAMILY_IPV4);
if (!link->dhcp_client) {
r = sd_dhcp_client_new(&link->dhcp_client);
r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
if (r < 0)
return r;
}
@ -624,7 +624,12 @@ int dhcp4_configure(Link *link) {
return r;
}
if (link->network->dhcp_use_routes) {
/* NOTE: when using Anonymity Profiles, routes PRL options are sent
* by default, so they should not be added again here. */
/* NOTE: even if this variable is called "use", it also "sends" PRL
* options, maybe there should be a different configuration variable
* to send or not route options?. */
if (link->network->dhcp_use_routes && !link->network->dhcp_anonymize) {
r = sd_dhcp_client_set_request_option(link->dhcp_client,
SD_DHCP_OPTION_STATIC_ROUTE);
if (r < 0)

View file

@ -2916,7 +2916,7 @@ network_file_fail:
goto dhcp4_address_fail;
}
r = sd_dhcp_client_new(&link->dhcp_client);
r = sd_dhcp_client_new(&link->dhcp_client, link->network->dhcp_anonymize);
if (r < 0)
return log_link_error_errno(link, r, "Failed to create DHCPv4 client: %m");

View file

@ -101,6 +101,7 @@ DHCP.UseMTU, config_parse_bool,
DHCP.UseHostname, config_parse_bool, 0, offsetof(Network, dhcp_use_hostname)
DHCP.UseDomains, config_parse_dhcp_use_domains, 0, offsetof(Network, dhcp_use_domains)
DHCP.UseRoutes, config_parse_bool, 0, offsetof(Network, dhcp_use_routes)
DHCP.Anonymize, config_parse_bool, 0, offsetof(Network, dhcp_anonymize)
DHCP.SendHostname, config_parse_bool, 0, offsetof(Network, dhcp_send_hostname)
DHCP.Hostname, config_parse_hostname, 0, offsetof(Network, dhcp_hostname)
DHCP.RequestBroadcast, config_parse_bool, 0, offsetof(Network, dhcp_broadcast)

View file

@ -79,6 +79,46 @@ void network_config_section_free(NetworkConfigSection *cs) {
free(cs);
}
/* Set defaults following RFC7844 */
void network_apply_anonymize_if_set(Network *network) {
if (!network->dhcp_anonymize)
return;
/* RFC7844 3.7
SHOULD NOT send the Host Name option */
network->dhcp_send_hostname = false;
/* RFC7844 section 3.:
MAY contain the Client Identifier option
Section 3.5:
clients MUST use client identifiers based solely
on the link-layer address */
/* NOTE: Using MAC, as it does not reveal extra information,
* and some servers might not answer if this option is not sent */
network->dhcp_client_identifier = DHCP_CLIENT_ID_MAC;
/* RFC 7844 3.10:
SHOULD NOT use the Vendor Class Identifier option */
/* NOTE: it was not initiallized to any value in network_load_one. */
network->dhcp_vendor_class_identifier = false;
/* RFC7844 section 3.6.:
The client intending to protect its privacy SHOULD only request a
minimal number of options in the PRL and SHOULD also randomly shuffle
the ordering of option codes in the PRL. If this random ordering
cannot be implemented, the client MAY order the option codes in the
PRL by option code number (lowest to highest).
*/
/* NOTE: dhcp_use_mtu is false by default,
* though it was not initiallized to any value in network_load_one.
* Maybe there should be another var called *send*?
* (to use the MTU sent by the server but to do not send
* the option in the PRL). */
network->dhcp_use_mtu = false;
/* RFC7844 section 3.6.
* same comments as previous option */
network->dhcp_use_routes = false;
/* RFC7844 section 3.6.
* same comments as previous option */
network->dhcp_use_timezone = false;
}
static int network_load_one(Manager *manager, const char *filename) {
_cleanup_network_free_ Network *network = NULL;
_cleanup_fclose_ FILE *file = NULL;
@ -161,11 +201,24 @@ static int network_load_one(Manager *manager, const char *filename) {
network->dhcp_use_ntp = true;
network->dhcp_use_dns = true;
network->dhcp_use_hostname = true;
/* NOTE: this var might be overwriten by network_apply_anonymize_if_set */
network->dhcp_use_routes = true;
/* NOTE: this var might be overwriten by network_apply_anonymize_if_set */
network->dhcp_send_hostname = true;
/* To enable/disable RFC7844 Anonymity Profiles */
network->dhcp_anonymize = false;
network->dhcp_route_metric = DHCP_ROUTE_METRIC;
/* NOTE: this var might be overwrite by network_apply_anonymize_if_set */
network->dhcp_client_identifier = DHCP_CLIENT_ID_DUID;
network->dhcp_route_table = RT_TABLE_MAIN;
/* NOTE: the following vars were not set to any default,
* even if they are commented in the man?
* These vars might be overwriten by network_apply_anonymize_if_set */
network->dhcp_vendor_class_identifier = false;
/* NOTE: from man: UseMTU=... Defaults to false*/
network->dhcp_use_mtu = false;
/* NOTE: from man: UseTimezone=... Defaults to "no".*/
network->dhcp_use_timezone = false;
network->dhcp_server_emit_dns = true;
network->dhcp_server_emit_ntp = true;
@ -220,6 +273,8 @@ static int network_load_one(Manager *manager, const char *filename) {
if (r < 0)
return r;
network_apply_anonymize_if_set(network);
/* IPMasquerade=yes implies IPForward=yes */
if (network->ip_masquerade)
network->ip_forward |= ADDRESS_FAMILY_IPV4;

View file

@ -128,6 +128,7 @@ struct Network {
unsigned dhcp_route_metric;
uint32_t dhcp_route_table;
uint16_t dhcp_client_port;
bool dhcp_anonymize;
bool dhcp_send_hostname;
bool dhcp_broadcast;
bool dhcp_critical;
@ -251,6 +252,7 @@ int network_load(Manager *manager);
int network_get_by_name(Manager *manager, const char *name, Network **ret);
int network_get(Manager *manager, struct udev_device *device, const char *ifname, const struct ether_addr *mac, Network **ret);
int network_apply(Network *network, Link *link);
void network_apply_anonymize_if_set(Network *network);
bool network_has_static_ipv6_addresses(Network *network);

View file

@ -58,9 +58,17 @@ enum {
SD_DHCP_OPTION_INTERFACE_MTU_AGING_TIMEOUT = 24,
SD_DHCP_OPTION_INTERFACE_MTU = 26,
SD_DHCP_OPTION_BROADCAST = 28,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_ROUTER_DISCOVER = 31,
SD_DHCP_OPTION_STATIC_ROUTE = 33,
SD_DHCP_OPTION_NTP_SERVER = 42,
SD_DHCP_OPTION_VENDOR_SPECIFIC = 43,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_NAMESERVER = 44,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_NODETYPE = 46,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_NETBIOS_SCOPE = 47,
SD_DHCP_OPTION_REQUESTED_IP_ADDRESS = 50,
SD_DHCP_OPTION_IP_ADDRESS_LEASE_TIME = 51,
SD_DHCP_OPTION_OVERLOAD = 52,
@ -79,6 +87,10 @@ enum {
SD_DHCP_OPTION_DOMAIN_SEARCH_LIST = 119,
SD_DHCP_OPTION_CLASSLESS_STATIC_ROUTE = 121,
SD_DHCP_OPTION_PRIVATE_BASE = 224,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_PRIVATE_CLASSLESS_STATIC_ROUTE = 249,
/* Windows 10 option to send when Anonymize=true */
SD_DHCP_OPTION_PRIVATE_PROXY_AUTODISCOVERY = 252,
SD_DHCP_OPTION_PRIVATE_LAST = 254,
SD_DHCP_OPTION_END = 255,
};
@ -146,7 +158,9 @@ int sd_dhcp_client_start(sd_dhcp_client *client);
sd_dhcp_client *sd_dhcp_client_ref(sd_dhcp_client *client);
sd_dhcp_client *sd_dhcp_client_unref(sd_dhcp_client *client);
int sd_dhcp_client_new(sd_dhcp_client **ret);
/* NOTE: anonymize parameter is used to initialize PRL memory with different
* options when using RFC7844 Anonymity Profiles */
int sd_dhcp_client_new(sd_dhcp_client **ret, int anonymize);
int sd_dhcp_client_attach_event(
sd_dhcp_client *client,