1
0
mirror of https://github.com/fafhrd91/actix-web synced 2024-11-27 17:52:56 +01:00

sec fixes

This commit is contained in:
Rob Ede 2021-08-08 21:21:48 +01:00
parent 24d525d978
commit 655d7b4f05
No known key found for this signature in database
GPG Key ID: 97C636207D3EF933
2 changed files with 59 additions and 26 deletions

View File

@ -67,6 +67,7 @@ pub(crate) trait MessageType: Sized {
let mut has_upgrade_websocket = false; let mut has_upgrade_websocket = false;
let mut expect = false; let mut expect = false;
let mut chunked = false; let mut chunked = false;
let mut seen_te = false;
let mut content_length = None; let mut content_length = None;
{ {
@ -85,8 +86,17 @@ pub(crate) trait MessageType: Sized {
}; };
match name { match name {
header::CONTENT_LENGTH => { header::CONTENT_LENGTH if content_length.is_some() => {
if let Ok(s) = value.to_str() { debug!("multiple Content-Length");
return Err(ParseError::Header);
}
header::CONTENT_LENGTH => match value.to_str() {
Ok(s) if s.trim().starts_with("+") => {
debug!("illegal Content-Length: {:?}", s);
return Err(ParseError::Header);
}
Ok(s) => {
if let Ok(len) = s.parse::<u64>() { if let Ok(len) = s.parse::<u64>() {
if len != 0 { if len != 0 {
content_length = Some(len); content_length = Some(len);
@ -95,15 +105,31 @@ pub(crate) trait MessageType: Sized {
debug!("illegal Content-Length: {:?}", s); debug!("illegal Content-Length: {:?}", s);
return Err(ParseError::Header); return Err(ParseError::Header);
} }
} else { }
Err(_) => {
debug!("illegal Content-Length: {:?}", value); debug!("illegal Content-Length: {:?}", value);
return Err(ParseError::Header); return Err(ParseError::Header);
} }
} },
// transfer-encoding // transfer-encoding
header::TRANSFER_ENCODING if seen_te => {
debug!("multiple Transfer-Encoding not allowed");
return Err(ParseError::Header);
}
header::TRANSFER_ENCODING => { header::TRANSFER_ENCODING => {
seen_te = true;
if let Ok(s) = value.to_str().map(|s| s.trim()) { if let Ok(s) = value.to_str().map(|s| s.trim()) {
chunked = s.eq_ignore_ascii_case("chunked"); if s.eq_ignore_ascii_case("chunked") {
chunked = true;
} else if s.eq_ignore_ascii_case("identity") {
// allow silently since multiple TE headers are already checked
} else {
debug!("illegal Transfer-Encoding: {:?}", s);
return Err(ParseError::Header);
}
} else { } else {
return Err(ParseError::Header); return Err(ParseError::Header);
} }
@ -510,19 +536,11 @@ impl ChunkedState {
size: &mut u64, size: &mut u64,
) -> Poll<Result<ChunkedState, io::Error>> { ) -> Poll<Result<ChunkedState, io::Error>> {
let radix = 16; let radix = 16;
match byte!(rdr) {
b @ b'0'..=b'9' => { let rem = match byte!(rdr) {
*size *= radix; b @ b'0'..=b'9' => b - b'0',
*size += u64::from(b - b'0'); b @ b'a'..=b'f' => b + 10 - b'a',
} b @ b'A'..=b'F' => b + 10 - b'A',
b @ b'a'..=b'f' => {
*size *= radix;
*size += u64::from(b + 10 - b'a');
}
b @ b'A'..=b'F' => {
*size *= radix;
*size += u64::from(b + 10 - b'A');
}
b'\t' | b' ' => return Poll::Ready(Ok(ChunkedState::SizeLws)), b'\t' | b' ' => return Poll::Ready(Ok(ChunkedState::SizeLws)),
b';' => return Poll::Ready(Ok(ChunkedState::Extension)), b';' => return Poll::Ready(Ok(ChunkedState::Extension)),
b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)), b'\r' => return Poll::Ready(Ok(ChunkedState::SizeLf)),
@ -532,9 +550,24 @@ impl ChunkedState {
"Invalid chunk size line: Invalid Size", "Invalid chunk size line: Invalid Size",
))); )));
} }
} };
match size.checked_mul(radix) {
Some(n) => {
*size = n as u64;
*size += rem as u64;
Poll::Ready(Ok(ChunkedState::Size)) Poll::Ready(Ok(ChunkedState::Size))
} }
None => {
debug!("chunk size would overflow");
Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid chunk size line: Invalid Size",
)))
}
}
}
fn read_size_lws(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> { fn read_size_lws(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
trace!("read_size_lws"); trace!("read_size_lws");
@ -552,6 +585,11 @@ impl ChunkedState {
fn read_extension(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> { fn read_extension(rdr: &mut BytesMut) -> Poll<Result<ChunkedState, io::Error>> {
match byte!(rdr) { match byte!(rdr) {
b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)), b'\r' => Poll::Ready(Ok(ChunkedState::SizeLf)),
// strictly 0x20 (space) should be disallowed but we don't parse quoted strings here
0x00..=0x08 | 0x0a..=0x1f | 0x7f => Poll::Ready(Err(io::Error::new(
io::ErrorKind::InvalidInput,
"Invalid character in chunk extension",
))),
_ => Poll::Ready(Ok(ChunkedState::Extension)), // no supported extensions _ => Poll::Ready(Ok(ChunkedState::Extension)), // no supported extensions
} }
} }
@ -977,13 +1015,7 @@ mod tests {
"GET /test HTTP/1.1\r\n\ "GET /test HTTP/1.1\r\n\
transfer-encoding: chnked\r\n\r\n", transfer-encoding: chnked\r\n\r\n",
); );
let req = parse_ready!(&mut buf); expect_parse_err!(&mut buf);
if let Ok(val) = req.chunked() {
assert!(!val);
} else {
unreachable!("Error");
}
} }
#[test] #[test]

View File

@ -80,6 +80,7 @@ pub(crate) trait MessageType: Sized {
match length { match length {
BodySize::Stream => { BodySize::Stream => {
if chunked { if chunked {
skip_len = true;
if camel_case { if camel_case {
dst.put_slice(b"\r\nTransfer-Encoding: chunked\r\n") dst.put_slice(b"\r\nTransfer-Encoding: chunked\r\n")
} else { } else {