]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
dhcp6: reject IA_PD_PREFIX with invalid prefix length 42736/head
authorLuca Boccassi <luca.boccassi@gmail.com>
Wed, 24 Jun 2026 12:20:56 +0000 (13:20 +0100)
committerLuca Boccassi <luca.boccassi@gmail.com>
Thu, 25 Jun 2026 09:16:26 +0000 (10:16 +0100)
dhcp6_option_parse_ia_pdprefix() validates the lifetimes but never the
prefixlen byte, so a delegated prefix with prefixlen == 0 or > 128 is
stored in the lease and handed over.

RFC 8415 defines the prefix length as 1 to 128, and the send-side
option_append_pd_prefix() already rejects 0, so reject the out-of-range
values on the receive path too.

Follow-up for f8ad4dd45d761d839de49ddff9d7fbf46892c148

src/libsystemd-network/dhcp6-option.c
src/libsystemd-network/test-dhcp6-client.c

index d62fd70588923f8585b02872d3d3000a752370ad..1c6b29b8cb19d6e593a2f02bd35b1b8459a4b3ae 100644 (file)
@@ -672,6 +672,13 @@ static int dhcp6_option_parse_ia_pdprefix(sd_dhcp6_client *client, DHCP6IA *ia,
                                               FORMAT_TIMESPAN(lt_pref, USEC_PER_SEC),
                                               FORMAT_TIMESPAN(lt_valid, USEC_PER_SEC));
 
+        /* RFC 8415 defines the prefix length as 1…128; reject the out-of-range values the sender-side
+         * counterpart option_append_pd_prefix() also refuses. */
+        if (a->iapdprefix.prefixlen == 0 || a->iapdprefix.prefixlen > 128)
+                return log_dhcp6_client_errno(client, SYNTHETIC_ERRNO(EINVAL),
+                                              "Received a PD prefix with invalid prefix length %u, ignoring.",
+                                              a->iapdprefix.prefixlen);
+
         if (len > sizeof(struct iapdprefix)) {
                 r = dhcp6_option_parse_ia_options(client, data + sizeof(struct iapdprefix), len - sizeof(struct iapdprefix));
                 if (r < 0)
index f2b6ce8598be3b24bd5897460b2b6792737842a4..08dda7ce18720ef6f8c947e7c5a0069ec6a05ef2 100644 (file)
@@ -324,6 +324,17 @@ TEST(option_status) {
                 /* PD prefix status option */
                 0x00, 0x0d, 0x00, 0x02, 0x00, 0x00,
         };
+        static const uint8_t option6[] = {
+                /* IA PD */
+                0x00, 0x19, 0x00, 0x29, 0x1a, 0x1d, 0x1a, 0x1d,
+                0x00, 0x01, 0x00, 0x01, 0x00, 0x02, 0x00, 0x02,
+                /* IA PD Prefix, with an invalid prefix length of 0 */
+                0x00, 0x1a, 0x00, 0x19,
+                0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+                0x00, 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00,
+                0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+                0x00,
+        };
         _cleanup_(dhcp6_ia_freep) DHCP6IA *ia = NULL;
         DHCP6Option *option;
         be32_t iaid;
@@ -379,6 +390,14 @@ TEST(option_status) {
         assert_se(ia);
         assert_se(ia->addresses);
         ia = dhcp6_ia_free(ia);
+
+        /* An IA_PD whose only prefix carries an invalid (zero) prefix length must be refused, leaving no
+         * valid prefix behind. */
+        option = (DHCP6Option*) option6;
+        assert_se(sizeof(option6) == sizeof(DHCP6Option) + be16toh(option->len));
+        r = dhcp6_option_parse_ia(NULL, iaid, be16toh(option->code), be16toh(option->len), option->data, &ia);
+        assert_se(r == -ENODATA);
+        assert_se(!ia);
 }
 
 TEST(client_parse_message_issue_22099) {