Improve checking the size of post body. (#189)

This commit is contained in:
Tim Bruijnzeels
2020-03-16 13:04:29 -03:00
parent 4c716c8f09
commit 04acd3aa4a
4 changed files with 62 additions and 33 deletions
+10
View File
@@ -62,6 +62,12 @@ pub enum Error {
#[display(fmt = "Invalid path argument for seconds")]
ApiInvalidSeconds,
#[display(fmt = "POST body exceeds configured limit")]
PostTooBig,
#[display(fmt = "POST body cannot be read")]
PostCannotRead,
//-----------------------------------------------------------------
// Repository Issues
//-----------------------------------------------------------------
@@ -343,6 +349,10 @@ impl Error {
Error::ApiInvalidSeconds => ErrorResponse::new("api-invalid-path-seconds", &self),
Error::PostTooBig => ErrorResponse::new("api-post-body-exceeds-limit", &self),
Error::PostCannotRead => ErrorResponse::new("api-post-body-cannot-read", &self),
//-----------------------------------------------------------------
// Repository Issues (label: repo-*)
//-----------------------------------------------------------------
+6 -6
View File
@@ -82,15 +82,15 @@ impl ConfigDefaults {
600
}
fn post_limit_api() -> usize {
fn post_limit_api() -> u64 {
256 * 1024 // 256kB
}
fn post_limit_rfc8181() -> usize {
fn post_limit_rfc8181() -> u64 {
32 * 1024 * 1024 // 32MB (roughly 8000 issued certificates, so a key roll for nicbr and 100% uptake should be okay)
}
fn post_limit_rfc6492() -> usize {
fn post_limit_rfc6492() -> u64 {
1024 * 1024 // 1MB (for ref. the NIC br cert is about 200kB)
}
}
@@ -155,13 +155,13 @@ pub struct Config {
pub ca_refresh: u32,
#[serde(default = "ConfigDefaults::post_limit_api")]
pub post_limit_api: usize,
pub post_limit_api: u64,
#[serde(default = "ConfigDefaults::post_limit_rfc8181")]
pub post_limit_rfc8181: usize,
pub post_limit_rfc8181: u64,
#[serde(default = "ConfigDefaults::post_limit_rfc6492")]
pub post_limit_rfc6492: usize,
pub post_limit_rfc6492: u64,
}
/// # Accessors
+36 -17
View File
@@ -14,6 +14,7 @@ use crate::commons::error::Error;
use crate::commons::remote::{rfc6492, rfc8181};
use crate::daemon::auth::Auth;
use crate::daemon::http::server::State;
use std::convert::TryInto;
pub mod server;
pub mod statics;
@@ -232,45 +233,60 @@ impl Request {
///
/// Here we want to limit the bytes consumed to a maximum. So, the
/// code below is adapted from the method in the hyper crate.
pub async fn read_bytes(self, limit: usize) -> Result<Bytes, Error> {
pub async fn read_bytes(self, limit: u64) -> Result<Bytes, Error> {
let body = self.request.into_body();
futures_util::pin_mut!(body);
if body.size_hint().lower() > limit {
return Err(Error::PostTooBig);
}
let mut size_processed = 0;
fn assert_body_size(size: usize, limit: usize) -> Result<(), io::Error> {
if size > limit {
Err(io::Error::new(
io::ErrorKind::Other,
"Post exceeds max length",
))
fn assert_body_size(
size_processed: u64,
body_lower_hint: u64,
post_limit: u64,
) -> Result<(), Error> {
if size_processed + body_lower_hint > post_limit {
Err(Error::PostTooBig)
} else {
Ok(())
}
}
assert_body_size(size_processed, body.size_hint().lower(), limit)?;
// If there's only 1 chunk, we can just return Buf::to_bytes()
let mut first = if let Some(buf) = body.data().await {
let buf = buf.map_err(|_| Error::custom("Error reading body"))?;
let size = buf.bytes().len();
let buf = buf.map_err(|_| Error::PostCannotRead)?;
let size: u64 = buf
.bytes()
.len()
.try_into()
.map_err(|_| Error::PostTooBig)?;
size_processed += size;
assert_body_size(size_processed, limit)?;
buf
} else {
return Ok(Bytes::new());
};
assert_body_size(size_processed, body.size_hint().lower(), limit)?;
let second = if let Some(buf) = body.data().await {
let buf = buf.map_err(|_| Error::custom("Error reading body"))?;
let size = buf.bytes().len();
let buf = buf.map_err(|_| Error::PostCannotRead)?;
let size: u64 = buf
.bytes()
.len()
.try_into()
.map_err(|_| Error::PostTooBig)?;
size_processed += size;
assert_body_size(size_processed, limit)?;
buf
} else {
return Ok(first.to_bytes());
};
assert_body_size(size_processed, body.size_hint().lower(), limit)?;
// With more than 1 buf, we gotta flatten into a Vec first.
let cap = first.remaining() + second.remaining() + body.size_hint().lower() as usize;
let mut vec = Vec::with_capacity(cap);
@@ -278,11 +294,14 @@ impl Request {
vec.put(second);
while let Some(buf) = body.data().await {
let buf = buf.map_err(|_| Error::custom("Error reading body"))?;
let size = buf.bytes().len();
let buf = buf.map_err(|_| Error::PostCannotRead)?;
let size: u64 = buf
.bytes()
.len()
.try_into()
.map_err(|_| Error::PostTooBig)?;
size_processed += size;
assert_body_size(size_processed, limit)?;
assert_body_size(size_processed, body.size_hint().lower(), limit)?;
vec.put(buf);
}
+10 -10
View File
@@ -62,13 +62,13 @@ pub struct KrillServer {
}
pub struct PostLimits {
api: usize,
rfc6492: usize,
rfc8181: usize,
api: u64,
rfc6492: u64,
rfc8181: u64,
}
impl PostLimits {
fn new(api: usize, rfc6492: usize, rfc8181: usize) -> Self {
fn new(api: u64, rfc6492: u64, rfc8181: u64) -> Self {
PostLimits {
api,
rfc8181,
@@ -76,13 +76,13 @@ impl PostLimits {
}
}
pub fn api(&self) -> usize {
pub fn api(&self) -> u64 {
self.api
}
pub fn rfc6492(&self) -> usize {
pub fn rfc6492(&self) -> u64 {
self.rfc6492
}
pub fn rfc8181(&self) -> usize {
pub fn rfc8181(&self) -> u64 {
self.rfc8181
}
}
@@ -203,15 +203,15 @@ impl KrillServer {
self.authorizer.is_api_allowed(auth)
}
pub fn limit_api(&self) -> usize {
pub fn limit_api(&self) -> u64 {
self.post_limits.api()
}
pub fn limit_rfc8181(&self) -> usize {
pub fn limit_rfc8181(&self) -> u64 {
self.post_limits.rfc8181()
}
pub fn limit_rfc6492(&self) -> usize {
pub fn limit_rfc6492(&self) -> u64 {
self.post_limits.rfc6492()
}
}