aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoão Valverde <j@v6e.pt>2022-10-18 18:30:48 +0100
committerJoão Valverde <j@v6e.pt>2022-10-20 17:51:08 +0000
commit375cd6392ed811d0776d144eca9d1cf2f54fcfa0 (patch)
treed4a5f39f0da972356d43d06d98cc1bc7f3244911
parent4eb78424d28ca3148376b51744b0ad613aad0c39 (diff)
DHCPv6: Sanitize domain display labels for invalid UTF-8
Assume labels are encoded using ASCII. Validate them instead of copying raw byte strings from the TVBuff. Fixes #18398.
-rw-r--r--epan/dissectors/packet-dhcpv6.c100
1 files changed, 50 insertions, 50 deletions
diff --git a/epan/dissectors/packet-dhcpv6.c b/epan/dissectors/packet-dhcpv6.c
index f9e4347a97..a0ac75ae6e 100644
--- a/epan/dissectors/packet-dhcpv6.c
+++ b/epan/dissectors/packet-dhcpv6.c
@@ -1146,20 +1146,24 @@ dissect_packetcable_ccc_option(proto_tree *v_tree, proto_item *v_item, packet_in
* (a.) to be aware of relative/partial names
* (b.) to detect protocol violations per keywords "MUST" "REQUIRED" and "SHALL"
*/
+/*
+ * This function assumes labels are encoded using ASCII. While RFC 1305 section 3.1
+ * supposedly doesn't formally mandate any one encoding, from my understanding of it,
+ * ASCII is "assumed" (and a de facto requirement for interoperability).
+ * An expert info for invalid ASCII in domain name labels would be a useful enhancement.
+ */
static void
dhcpv6_domain(proto_tree *subtree, proto_item *v_item _U_, packet_info *pinfo, int hfindex,
tvbuff_t *tvb, int dn_field_off, guint16 dn_field_len)
{
int final_field_off; /* Last offset of in DN field */
+ guint8 *label_str;
guint8 label_len;
int remlen; /* The number of remaining octets in a domain field */
guint8 num_labels;
int first_lab_off; /* Offset of the first label of a DN */
- char decoded_name_str[320]; /* Array used to construct an FQDN or partial name. Although 255
- is the max allowed FQDN length, the array is longer in order
- to accommodate FQDNs greater than 255 for display in error
- messages. */
- int dpos; /* Current position within decoded_name_str[] */
+ wmem_strbuf_t *decoded_name_buf; /* Array used to construct an FQDN or partial name. */
+ int total_label_ascii_len; /* Accumulated count of decoded label bytes, including separators. */
int offset;
gboolean fqdn_seen, inc;
proto_item *exi;
@@ -1181,8 +1185,8 @@ dhcpv6_domain(proto_tree *subtree, proto_item *v_item _U_, packet_info *pinfo, i
final_field_off = dn_field_off + dn_field_len - 1;
remlen = dn_field_len;
num_labels = 0;
- dpos = 0;
- decoded_name_str[0] = '\0';
+ total_label_ascii_len = 0;
+ decoded_name_buf = wmem_strbuf_new(pinfo->pool, NULL);
fqdn_seen = FALSE;
inc = TRUE;
@@ -1207,18 +1211,16 @@ dhcpv6_domain(proto_tree *subtree, proto_item *v_item _U_, packet_info *pinfo, i
ex_subtree = proto_item_add_subtree(exi, ett_clientfqdn_expert);
if (num_labels) {
- decoded_name_str[dpos] = '\0';
proto_tree_add_string_format(ex_subtree, hf_dhcpv6_decoded_portion, tvb,
- first_lab_off, dpos, decoded_name_str,
- "The decoded portion of this FQDN to this point is [%s]\n", decoded_name_str);
+ first_lab_off, total_label_ascii_len, decoded_name_buf->str,
+ "The decoded portion of this FQDN to this point is [%s]\n", decoded_name_buf->str);
}
proto_tree_add_expert(ex_subtree, pinfo, &ei_dhcpv6_non_dns_encoded_name, tvb, offset, 1);
return;
}
- if(!dpos)
+ if(total_label_ascii_len == 0)
first_lab_off = offset;
- decoded_name_str[dpos] = '\0';
offset++;
remlen--;
@@ -1235,17 +1237,16 @@ dhcpv6_domain(proto_tree *subtree, proto_item *v_item _U_, packet_info *pinfo, i
ex_subtree = proto_item_add_subtree(exi, ett_clientfqdn_expert);
if (num_labels) {
- decoded_name_str[dpos] = '\0';
proto_tree_add_string_format(ex_subtree, hf_dhcpv6_decoded_portion, tvb,
- first_lab_off, dpos, decoded_name_str,
- "The successfully decoded portion of this FQDN: [%s]\n", decoded_name_str);
+ first_lab_off, total_label_ascii_len, decoded_name_buf->str,
+ "The successfully decoded portion of this FQDN: [%s]\n", decoded_name_buf->str);
}
proto_tree_add_expert(ex_subtree, pinfo, &ei_dhcpv6_domain_field_len_exceeded, tvb,
dn_field_off, dn_field_len);
return;
}
- if (dpos + label_len + 2 > 255) {
+ if (total_label_ascii_len + label_len + 2 > 255) {
/*
* RFC 1034 Section 3.1: "To simplify implementations, the total number of octets that
* represent a domain name (i.e., the sum of all label octets and label lengths) is
@@ -1256,33 +1257,32 @@ dhcpv6_domain(proto_tree *subtree, proto_item *v_item _U_, packet_info *pinfo, i
* Since label_len is valid (<=63 and the name has not been truncated (i.e., its length
* is <= remlen), display this oversized FQDN.
*/
- decoded_name_str[dpos] = '.';
- dpos++;
- tvb_memcpy(tvb, decoded_name_str + dpos, offset, label_len);
+ wmem_strbuf_append_c(decoded_name_buf, '.');
+ total_label_ascii_len++;
+ label_str = tvb_get_string_enc(pinfo->pool, tvb, offset, label_len, ENC_ASCII);
+ wmem_strbuf_append(decoded_name_buf, label_str);
offset += label_len;
- dpos += label_len;
+ total_label_ascii_len += label_len;
if (tvb_get_guint8(tvb, offset) == 0) {
- decoded_name_str[dpos] = '.';
- dpos++;
+ wmem_strbuf_append_c(decoded_name_buf, '.');
+ total_label_ascii_len++;
offset++;
inc = FALSE;
}
- decoded_name_str[dpos] = '\0';
exi = proto_tree_add_uint_format(subtree, hf_dhcpv6_encoded_fqdn_len_gt_255, tvb,
- first_lab_off, dpos-1, dpos,
+ first_lab_off, total_label_ascii_len-1, total_label_ascii_len,
"FQDN: %s%s\n"
"ERROR: The total length of DNS-encoded names of this FQDN, %d, exceeds 255,\n"
- "the maximum allowed.", decoded_name_str, (inc ? "<incomplete>" : " "), dpos);
+ "the maximum allowed.", decoded_name_buf->str, (inc ? "<incomplete>" : " "), total_label_ascii_len);
ex_subtree = proto_item_add_subtree(exi, ett_clientfqdn_expert);
proto_tree_add_expert(ex_subtree, pinfo, &ei_dhcpv6_encoded_fqdn_len_gt_255, tvb,
- first_lab_off, dpos-1);
+ first_lab_off, total_label_ascii_len-1);
return;
}
if (label_len==0) {
- decoded_name_str[dpos] = '.';
- dpos++;
- decoded_name_str[dpos] = '\0';
+ wmem_strbuf_append_c(decoded_name_buf, '.');
+ total_label_ascii_len++;
if (num_labels == 0) {
/*
@@ -1302,13 +1302,13 @@ dhcpv6_domain(proto_tree *subtree, proto_item *v_item _U_, packet_info *pinfo, i
* TLDs consist of one DNS encoded label and a root label(0) (e.g., [com.] is
* encoded as [03 64 6F 6D 00]).
*/
- exi = proto_tree_add_string_format(subtree, hf_dhcpv6_tld, tvb, first_lab_off, dpos+1,
- decoded_name_str, "Top Level Domain name (TLD): %s", decoded_name_str);
+ exi = proto_tree_add_string_format(subtree, hf_dhcpv6_tld, tvb, first_lab_off, total_label_ascii_len+1,
+ decoded_name_buf->str, "Top Level Domain name (TLD): %s", decoded_name_buf->str);
ex_subtree = proto_item_add_subtree(exi, ett_clientfqdn_expert);
- proto_tree_add_expert(ex_subtree, pinfo, &ei_dhcpv6_tld_lookup, tvb, first_lab_off, dpos+1);
+ proto_tree_add_expert(ex_subtree, pinfo, &ei_dhcpv6_tld_lookup, tvb, first_lab_off, total_label_ascii_len+1);
num_labels = 0;
- dpos = 0;
+ total_label_ascii_len = 0;
fqdn_seen = TRUE;
continue; /* This was only a COMMENT/WARNING so continue */
}
@@ -1317,9 +1317,9 @@ dhcpv6_domain(proto_tree *subtree, proto_item *v_item _U_, packet_info *pinfo, i
* indicate that the domain name is in fact an FQDN and not a multi-part partially
* qualified domain name.
*/
- proto_tree_add_string(subtree, hfindex, tvb, first_lab_off, dpos+1, decoded_name_str);
+ proto_tree_add_string(subtree, hfindex, tvb, first_lab_off, total_label_ascii_len+1, decoded_name_buf->str);
num_labels = 0;
- dpos = 0;
+ total_label_ascii_len = 0;
fqdn_seen = TRUE;
continue; /* Decode the next FQDN, if any */
}
@@ -1341,9 +1341,9 @@ dhcpv6_domain(proto_tree *subtree, proto_item *v_item _U_, packet_info *pinfo, i
* for more info.
*/
if (offset + label_len - 1 == final_field_off) {
- tvb_memcpy(tvb, decoded_name_str + dpos, first_lab_off + 1, label_len);
- dpos += label_len;
- decoded_name_str[dpos] = '\0';
+ label_str = tvb_get_string_enc(pinfo->pool, tvb, first_lab_off + 1, label_len, ENC_ASCII);
+ wmem_strbuf_append(decoded_name_buf, label_str);
+ total_label_ascii_len += label_len;
num_labels++;
if (fqdn_seen) {
@@ -1354,10 +1354,10 @@ dhcpv6_domain(proto_tree *subtree, proto_item *v_item _U_, packet_info *pinfo, i
col_append_str(pinfo->cinfo, COL_INFO, " [PROTOCOL VIOLATION]");
exi = proto_tree_add_string_format(subtree, hf_dhcpv6_partial_name_preceded_by_fqdn,
- tvb, first_lab_off, label_len, decoded_name_str,
+ tvb, first_lab_off, label_len, decoded_name_buf->str,
"Partial name: %s\n"
"ERROR: A single or multi-part partial name must be the only name in "
- "the domain field", decoded_name_str);
+ "the domain field", decoded_name_buf->str);
ex_subtree = proto_item_add_subtree(exi, ett_clientfqdn_expert);
proto_tree_add_expert(ex_subtree, pinfo, &ei_dhcpv6_partial_name_preceded_by_fqdn,
tvb, first_lab_off, label_len);
@@ -1367,13 +1367,13 @@ dhcpv6_domain(proto_tree *subtree, proto_item *v_item _U_, packet_info *pinfo, i
/* A conformant partial name
*/
if (num_labels==1) {
- proto_tree_add_string_format(subtree, hfindex, tvb, first_lab_off, dpos+1,
- decoded_name_str, "Partial domain name: %s", decoded_name_str);
+ proto_tree_add_string_format(subtree, hfindex, tvb, first_lab_off, total_label_ascii_len+1,
+ decoded_name_buf->str, "Partial domain name: %s", decoded_name_buf->str);
}
else {
- proto_tree_add_string_format(subtree, hfindex, tvb, first_lab_off, dpos+1,
- decoded_name_str, "Multi-part partially qualified Domain Name: %s",
- decoded_name_str);
+ proto_tree_add_string_format(subtree, hfindex, tvb, first_lab_off, total_label_ascii_len+1,
+ decoded_name_buf->str, "Multi-part partially qualified Domain Name: %s",
+ decoded_name_buf->str);
}
return;
}
@@ -1383,14 +1383,14 @@ dhcpv6_domain(proto_tree *subtree, proto_item *v_item _U_, packet_info *pinfo, i
* trailing root label (0) is decoded as a dot. This is handled above.
*/
if (num_labels) {
- decoded_name_str[dpos] = '.';
- dpos++;
+ wmem_strbuf_append_c(decoded_name_buf, '.');
+ total_label_ascii_len++;
}
- tvb_memcpy(tvb, decoded_name_str + dpos, offset, label_len);
+ label_str = tvb_get_string_enc(pinfo->pool, tvb, offset, label_len, ENC_ASCII);
+ wmem_strbuf_append(decoded_name_buf, label_str);
offset += label_len;
remlen -= label_len;
- dpos += label_len;
- decoded_name_str[dpos] = '\0';
+ total_label_ascii_len += label_len;
num_labels++;
}
/* End of while() loop */