Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v247] sd-dhcp6-client: ignore IAs whose IAID do not match client's IAID #136

Merged
merged 2 commits into from
Oct 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ conf.set('BUILD_MODE', 'BUILD_MODE_' + get_option('mode').to_upper(),

want_ossfuzz = get_option('oss-fuzz')
want_libfuzzer = get_option('llvm-fuzz')
if want_ossfuzz + want_libfuzzer > 1
if want_ossfuzz and want_libfuzzer
error('only one of oss-fuzz or llvm-fuzz can be specified')
endif

Expand Down
2 changes: 1 addition & 1 deletion src/libsystemd-network/dhcp6-internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ int dhcp6_option_append_vendor_option(uint8_t **buf, size_t *buflen, OrderedHash
int dhcp6_option_parse(uint8_t **buf, size_t *buflen, uint16_t *optcode,
size_t *optlen, uint8_t **optvalue);
int dhcp6_option_parse_status(DHCP6Option *option, size_t len);
int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code);
int dhcp6_option_parse_ia(DHCP6Option *iaoption, be32_t iaid, DHCP6IA *ia, uint16_t *ret_status_code);
int dhcp6_option_parse_ip6addrs(uint8_t *optval, uint16_t optlen,
struct in6_addr **addrs, size_t count,
size_t *allocated);
Expand Down
35 changes: 32 additions & 3 deletions src/libsystemd-network/dhcp6-option.c
Original file line number Diff line number Diff line change
Expand Up @@ -515,7 +515,12 @@ static int dhcp6_option_parse_pdprefix(DHCP6Option *option, DHCP6IA *ia, uint32_
return 0;
}

int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_status_code) {
int dhcp6_option_parse_ia(
DHCP6Option *iaoption,
be32_t iaid,
DHCP6IA *ia,
uint16_t *ret_status_code) {

uint32_t lt_t1, lt_t2, lt_valid = 0, lt_min = UINT32_MAX;
uint16_t iatype, optlen;
size_t iaaddr_offset;
Expand All @@ -535,6 +540,14 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
if (len < DHCP6_OPTION_IA_NA_LEN)
return -ENOBUFS;

/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
* but not necessary to ignore or refuse the whole message. */
if (((const struct ia_na*) iaoption->data)->id != iaid)
/* ENOANO indicates the option should be ignored. */
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
"Received an IA_NA option with a different IAID "
"from the one chosen by the client, ignoring.");

iaaddr_offset = DHCP6_OPTION_IA_NA_LEN;
memcpy(&ia->ia_na, iaoption->data, sizeof(ia->ia_na));

Expand All @@ -553,6 +566,14 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
if (len < sizeof(ia->ia_pd))
return -ENOBUFS;

/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
* but not necessary to ignore or refuse the whole message. */
if (((const struct ia_pd*) iaoption->data)->id != iaid)
/* ENOANO indicates the option should be ignored. */
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
"Received an IA_PD option with a different IAID "
"from the one chosen by the client, ignoring.");

iaaddr_offset = sizeof(ia->ia_pd);
memcpy(&ia->ia_pd, iaoption->data, sizeof(ia->ia_pd));

Expand All @@ -570,13 +591,21 @@ int dhcp6_option_parse_ia(DHCP6Option *iaoption, DHCP6IA *ia, uint16_t *ret_stat
if (len < DHCP6_OPTION_IA_TA_LEN)
return -ENOBUFS;

/* According to RFC8415, IAs which do not match the client's IAID should be ignored,
* but not necessary to ignore or refuse the whole message. */
if (((const struct ia_ta*) iaoption->data)->id != iaid)
/* ENOANO indicates the option should be ignored. */
return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(ENOANO),
"Received an IA_TA option with a different IAID "
"from the one chosen by the client, ignoring.");

iaaddr_offset = DHCP6_OPTION_IA_TA_LEN;
memcpy(&ia->ia_ta.id, iaoption->data, sizeof(ia->ia_ta));
memcpy(&ia->ia_ta, iaoption->data, sizeof(ia->ia_ta));

break;

default:
return -ENOMSG;
return -EINVAL;
}

ia->type = iatype;
Expand Down
29 changes: 4 additions & 25 deletions src/libsystemd-network/sd-dhcp6-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -1102,7 +1102,6 @@ static int client_parse_message(
while (pos < len) {
DHCP6Option *option = (DHCP6Option *) &message->options[pos];
uint16_t optcode, optlen;
be32_t iaid_lease;
int status;
uint8_t *optval;

Expand Down Expand Up @@ -1181,25 +1180,15 @@ static int client_parse_message(
break;
}

r = dhcp6_option_parse_ia(option, &lease->ia, &ia_na_status);
if (r < 0 && r != -ENOMSG)
r = dhcp6_option_parse_ia(option, client->ia_pd.ia_na.id, &lease->ia, &ia_na_status);
if (r < 0 && r != -ENOANO)
return r;

if (ia_na_status == DHCP6_STATUS_NO_ADDRS_AVAIL) {
pos += offsetof(DHCP6Option, data) + optlen;
continue;
}

r = dhcp6_lease_get_iaid(lease, &iaid_lease);
if (r < 0)
return r;

if (client->ia_na.ia_na.id != iaid_lease) {
log_dhcp6_client(client, "%s has wrong IAID for IA NA",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}

if (lease->ia.addresses) {
lt_t1 = MIN(lt_t1, be32toh(lease->ia.ia_na.lifetime_t1));
lt_t2 = MIN(lt_t2, be32toh(lease->ia.ia_na.lifetime_t1));
Expand All @@ -1214,25 +1203,15 @@ static int client_parse_message(
break;
}

r = dhcp6_option_parse_ia(option, &lease->pd, &ia_pd_status);
if (r < 0 && r != -ENOMSG)
r = dhcp6_option_parse_ia(option, client->ia_pd.ia_pd.id, &lease->pd, &ia_pd_status);
if (r < 0 && r != -ENOANO)
return r;

if (ia_pd_status == DHCP6_STATUS_NO_PREFIX_AVAIL) {
pos += offsetof(DHCP6Option, data) + optlen;
continue;
}

r = dhcp6_lease_get_pd_iaid(lease, &iaid_lease);
if (r < 0)
return r;

if (client->ia_pd.ia_pd.id != iaid_lease) {
log_dhcp6_client(client, "%s has wrong IAID for IA PD",
dhcp6_message_type_to_string(message->type));
return -EINVAL;
}

if (lease->pd.addresses) {
lt_t1 = MIN(lt_t1, be32toh(lease->pd.ia_pd.lifetime_t1));
lt_t2 = MIN(lt_t2, be32toh(lease->pd.ia_pd.lifetime_t2));
Expand Down
39 changes: 26 additions & 13 deletions src/libsystemd-network/test-dhcp6-client.c
Original file line number Diff line number Diff line change
Expand Up @@ -287,41 +287,47 @@ static int test_option_status(sd_event *e) {
};
DHCP6Option *option;
DHCP6IA ia, pd;
be32_t iaid;
int r = 0;

log_debug("/* %s */", __func__);

memcpy(&iaid, option1 + 4, sizeof(iaid));

zero(ia);
option = (DHCP6Option *)option1;
assert_se(sizeof(option1) == sizeof(DHCP6Option) + be16toh(option->len));

r = dhcp6_option_parse_ia(option, &ia, NULL);
r = dhcp6_option_parse_ia(option, 0, &ia, NULL);
assert_se(r == -ENOANO);

r = dhcp6_option_parse_ia(option, iaid, &ia, NULL);
assert_se(r == 0);
assert_se(ia.addresses == NULL);

option->len = htobe16(17);
r = dhcp6_option_parse_ia(option, &ia, NULL);
r = dhcp6_option_parse_ia(option, iaid, &ia, NULL);
assert_se(r == -ENOBUFS);
assert_se(ia.addresses == NULL);

option->len = htobe16(sizeof(DHCP6Option));
r = dhcp6_option_parse_ia(option, &ia, NULL);
r = dhcp6_option_parse_ia(option, iaid, &ia, NULL);
assert_se(r == -ENOBUFS);
assert_se(ia.addresses == NULL);

zero(ia);
option = (DHCP6Option *)option2;
assert_se(sizeof(option2) == sizeof(DHCP6Option) + be16toh(option->len));

r = dhcp6_option_parse_ia(option, &ia, NULL);
r = dhcp6_option_parse_ia(option, iaid, &ia, NULL);
assert_se(r >= 0);
assert_se(ia.addresses == NULL);

zero(ia);
option = (DHCP6Option *)option3;
assert_se(sizeof(option3) == sizeof(DHCP6Option) + be16toh(option->len));

r = dhcp6_option_parse_ia(option, &ia, NULL);
r = dhcp6_option_parse_ia(option, iaid, &ia, NULL);
assert_se(r >= 0);
assert_se(ia.addresses != NULL);
dhcp6_lease_free_ia(&ia);
Expand All @@ -330,7 +336,7 @@ static int test_option_status(sd_event *e) {
option = (DHCP6Option *)option4;
assert_se(sizeof(option4) == sizeof(DHCP6Option) + be16toh(option->len));

r = dhcp6_option_parse_ia(option, &pd, NULL);
r = dhcp6_option_parse_ia(option, iaid, &pd, NULL);
assert_se(r >= 0);
assert_se(pd.addresses != NULL);
assert_se(memcmp(&pd.ia_pd.id, &option4[4], 4) == 0);
Expand All @@ -342,7 +348,7 @@ static int test_option_status(sd_event *e) {
option = (DHCP6Option *)option5;
assert_se(sizeof(option5) == sizeof(DHCP6Option) + be16toh(option->len));

r = dhcp6_option_parse_ia(option, &pd, NULL);
r = dhcp6_option_parse_ia(option, iaid, &pd, NULL);
assert_se(r >= 0);
assert_se(pd.addresses != NULL);
dhcp6_lease_free_ia(&pd);
Expand Down Expand Up @@ -447,23 +453,24 @@ static int test_advertise_option(sd_event *e) {
opt_clientid = true;
break;

case SD_DHCP6_OPTION_IA_NA:
case SD_DHCP6_OPTION_IA_NA: {
be32_t iaid = htobe32(0x0ecfa37d);

assert_se(optlen == 94);
assert_se(!memcmp(optval, &msg_advertise[26], optlen));

val = htobe32(0x0ecfa37d);
assert_se(!memcmp(optval, &val, sizeof(val)));
assert_se(!memcmp(optval, &iaid, sizeof(val)));

val = htobe32(80);
assert_se(!memcmp(optval + 4, &val, sizeof(val)));

val = htobe32(120);
assert_se(!memcmp(optval + 8, &val, sizeof(val)));

assert_se(dhcp6_option_parse_ia(option, &lease->ia, NULL) >= 0);
assert_se(dhcp6_option_parse_ia(option, iaid, &lease->ia, NULL) >= 0);

break;

}
case SD_DHCP6_OPTION_SERVERID:
assert_se(optlen == 14);
assert_se(!memcmp(optval, &msg_advertise[179], optlen));
Expand Down Expand Up @@ -596,6 +603,8 @@ static void test_client_solicit_cb(sd_dhcp6_client *client, int event,
static int test_client_send_reply(DHCP6Message *request) {
DHCP6Message reply;

log_debug("/* %s */", __func__);

reply.transaction_id = request->transaction_id;
reply.type = DHCP6_REPLY;

Expand Down Expand Up @@ -656,7 +665,7 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) {
assert_se(!memcmp(optval + 8, &val, sizeof(val)));

/* Then, this should refuse all addresses. */
assert_se(dhcp6_option_parse_ia(option, &lease->ia, NULL) >= 0);
assert_se(dhcp6_option_parse_ia(option, test_iaid, &lease->ia, NULL) >= 0);

break;

Expand Down Expand Up @@ -702,6 +711,8 @@ static int test_client_verify_request(DHCP6Message *request, size_t len) {
static int test_client_send_advertise(DHCP6Message *solicit) {
DHCP6Message advertise;

log_debug("/* %s */", __func__);

advertise.transaction_id = solicit->transaction_id;
advertise.type = DHCP6_ADVERTISE;

Expand Down Expand Up @@ -897,6 +908,8 @@ int dhcp6_network_send_udp_socket(int s, struct in6_addr *server_address,
IN6ADDR_ALL_DHCP6_RELAY_AGENTS_AND_SERVERS_INIT;
DHCP6Message *message;

log_debug("/* %s */", __func__);

assert_se(s == test_dhcp_fd[0]);
assert_se(server_address);
assert_se(packet);
Expand Down