dhcp: validate address time option lengths

DHCP lease, renewal, and rebinding time options contain a four-byte value,
but the parser previously ignored their declared length and always consumed
four bytes.

Reject these options unless their length is exactly four to
prevent parser desynchronization and incorrect keyword matches.

Ticket #8873
pull/16213/head
Giuseppe Longo 3 months ago committed by Victor Julien
parent a06e7c7422
commit 382b18da9b

@ -81,6 +81,7 @@ pub struct DHCPOption {
pub code: u8,
pub data: Option<Vec<u8>>,
pub option: DHCPOptionWrapper,
pub malformed: bool,
}
pub fn parse_header(i: &[u8]) -> IResult<&[u8], DHCPHeader> {
@ -135,13 +136,19 @@ pub fn parse_clientid_option(i: &[u8]) -> IResult<&[u8], DHCPOption> {
htype: 1,
data: data.to_vec(),
}),
malformed: false,
},
))
}
pub fn parse_address_time_option(i: &[u8]) -> IResult<&[u8], DHCPOption> {
let (i, code) = be_u8(i)?;
let (i, _len) = be_u8(i)?;
pub fn parse_address_time_option(i0: &[u8]) -> IResult<&[u8], DHCPOption> {
let (i, code) = be_u8(i0)?;
let (i, len) = be_u8(i)?;
if len != 4 {
let (i, mut opt) = parse_generic_option(i0)?;
opt.malformed = true;
return Ok((i, opt));
}
let (i, seconds) = be_u32(i)?;
Ok((
i,
@ -149,6 +156,7 @@ pub fn parse_address_time_option(i: &[u8]) -> IResult<&[u8], DHCPOption> {
code,
data: None,
option: DHCPOptionWrapper::TimeValue(DHCPOptTimeValue { seconds }),
malformed: false,
},
))
}
@ -165,6 +173,7 @@ pub fn parse_generic_option(i: &[u8]) -> IResult<&[u8], DHCPOption> {
option: DHCPOptionWrapper::Generic(DHCPOptGeneric {
data: data.to_vec(),
}),
malformed: false,
},
))
}
@ -180,6 +189,7 @@ pub fn parse_option(i: &[u8]) -> IResult<&[u8], DHCPOption> {
code: opt,
data: None,
option: DHCPOptionWrapper::Pad,
malformed: false,
},
)),
DHCP_OPT_END => {
@ -193,6 +203,7 @@ pub fn parse_option(i: &[u8]) -> IResult<&[u8], DHCPOption> {
code,
data: Some(data.to_vec()),
option: DHCPOptionWrapper::End,
malformed: false,
},
))
}
@ -217,11 +228,14 @@ fn find_overload_value(options: &[DHCPOption]) -> u8 {
0
}
fn parse_overloaded_field(field: &[u8], options: &mut Vec<DHCPOption>) {
fn parse_overloaded_field(
field: &[u8], options: &mut Vec<DHCPOption>, malformed_options: &mut bool,
) {
let mut next = field;
while !next.is_empty() {
match parse_option(next) {
Ok((rem, option)) => {
*malformed_options |= option.malformed;
let done = option.code == DHCP_OPT_END;
options.push(option);
next = rem;
@ -239,11 +253,12 @@ pub fn parse_dhcp(input: &[u8]) -> IResult<&[u8], DHCPMessage> {
Ok((rem, header)) => {
let mut options = Vec::new();
let mut next = rem;
let malformed_options = false;
let mut malformed_options = false;
let mut truncated_options = false;
loop {
match parse_option(next) {
Ok((rem, option)) => {
malformed_options |= option.malformed;
let done = option.code == DHCP_OPT_END;
options.push(option);
next = rem;
@ -259,10 +274,10 @@ pub fn parse_dhcp(input: &[u8]) -> IResult<&[u8], DHCPMessage> {
}
let overload = find_overload_value(&options);
if overload & 0x01 != 0 {
parse_overloaded_field(&header.bootfilename, &mut options);
parse_overloaded_field(&header.bootfilename, &mut options, &mut malformed_options);
}
if overload & 0x02 != 0 {
parse_overloaded_field(&header.servername, &mut options);
parse_overloaded_field(&header.servername, &mut options, &mut malformed_options);
}
let message = DHCPMessage {

Loading…
Cancel
Save