From 8efabc60f87e5462b01f4832d575f68382929624 Mon Sep 17 00:00:00 2001 From: Lars Wirzenius Date: Mon, 4 Jan 2021 18:57:49 +0200 Subject: feat: verify checksum of chunks downloaded from server --- src/bin/obnam.rs | 7 ++++++- src/chunkid.rs | 8 ++++++-- src/client.rs | 19 ++++++++++++++++++- src/cmd/get_chunk.rs | 15 +++++++++++++++ src/cmd/mod.rs | 3 +++ src/error.rs | 6 ++++++ 6 files changed, 54 insertions(+), 4 deletions(-) create mode 100644 src/cmd/get_chunk.rs (limited to 'src') diff --git a/src/bin/obnam.rs b/src/bin/obnam.rs index f31884b..e5703ed 100644 --- a/src/bin/obnam.rs +++ b/src/bin/obnam.rs @@ -2,7 +2,7 @@ use log::{debug, error, info, LevelFilter}; use log4rs::append::file::FileAppender; use log4rs::config::{Appender, Config, Logger, Root}; use obnam::client::ClientConfig; -use obnam::cmd::{backup, list, list_files, restore}; +use obnam::cmd::{backup, get_chunk, list, list_files, restore}; use std::path::{Path, PathBuf}; use structopt::StructOpt; @@ -23,6 +23,7 @@ fn main() -> anyhow::Result<()> { Command::List => list(&config), Command::ListFiles { gen_id } => list_files(&config, &gen_id), Command::Restore { gen_id, to } => restore(&config, &gen_id, &to), + Command::GetChunk { chunk_id } => get_chunk(&config, &chunk_id), }; if let Err(ref e) = result { @@ -60,6 +61,10 @@ enum Command { #[structopt(parse(from_os_str))] to: PathBuf, }, + GetChunk { + #[structopt()] + chunk_id: String, + }, } fn setup_logging(filename: &Path) -> anyhow::Result<()> { diff --git a/src/chunkid.rs b/src/chunkid.rs index 9eec41f..3933d4b 100644 --- a/src/chunkid.rs +++ b/src/chunkid.rs @@ -37,6 +37,10 @@ impl ChunkId { } } + pub fn from_str(s: &str) -> Self { + ChunkId { id: s.to_string() } + } + pub fn as_bytes(&self) -> &[u8] { self.id.as_bytes() } @@ -81,7 +85,7 @@ impl FromStr for ChunkId { type Err = (); fn from_str(s: &str) -> Result { - Ok(ChunkId { id: s.to_string() }) + Ok(ChunkId::from_str(s)) } } @@ -113,6 +117,6 @@ mod test { fn survives_round_trip() { let id = ChunkId::new(); let id_str = id.to_string(); - assert_eq!(id, id_str.parse().unwrap()); + assert_eq!(id, ChunkId::from_str(&id_str)) } } diff --git a/src/client.rs b/src/client.rs index 616ceef..6b1bb80 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,6 +4,7 @@ use crate::chunk::GenerationChunk; use crate::chunker::Chunker; use crate::chunkid::ChunkId; use crate::chunkmeta::ChunkMeta; +use crate::error::ObnamError; use crate::fsentry::{FilesystemEntry, FilesystemKind}; use crate::generation::{FinishedGeneration, LocalGeneration}; use crate::genlist::GenerationList; @@ -212,8 +213,24 @@ impl BackupClient { return Err(ClientError::ChunkNotFound(chunk_id.to_string()).into()); } + let headers = res.headers(); + let meta = headers.get("chunk-meta"); + if meta.is_none() { + return Err(ObnamError::NoChunkMeta(chunk_id.to_string()).into()); + } + let meta = meta.unwrap().to_str()?; + let meta: ChunkMeta = serde_json::from_str(meta)?; + let body = res.bytes()?; - Ok(DataChunk::new(body.to_vec())) + let body = body.to_vec(); + let actual = sha256(&body); + if actual != meta.sha256() { + return Err(ObnamError::WrongChecksum(chunk_id.to_string()).into()); + } + + let chunk: DataChunk = DataChunk::new(body); + + Ok(chunk) } fn fetch_generation_chunk(&self, gen_id: &str) -> anyhow::Result { diff --git a/src/cmd/get_chunk.rs b/src/cmd/get_chunk.rs new file mode 100644 index 0000000..bf653ff --- /dev/null +++ b/src/cmd/get_chunk.rs @@ -0,0 +1,15 @@ +use crate::chunkid::ChunkId; +use crate::client::BackupClient; +use crate::client::ClientConfig; +use std::io::{stdout, Write}; + +pub fn get_chunk(config: &ClientConfig, chunk_id: &str) -> anyhow::Result<()> { + let client = BackupClient::new(&config.server_url)?; + let chunk_id: ChunkId = chunk_id.parse().unwrap(); + let chunk = client.fetch_chunk(&chunk_id)?; + + let stdout = stdout(); + let mut handle = stdout.lock(); + handle.write_all(chunk.data())?; + Ok(()) +} diff --git a/src/cmd/mod.rs b/src/cmd/mod.rs index 2919d88..c3cebe2 100644 --- a/src/cmd/mod.rs +++ b/src/cmd/mod.rs @@ -9,3 +9,6 @@ pub use list_files::list_files; pub mod restore; pub use restore::restore; + +pub mod get_chunk; +pub use get_chunk::get_chunk; diff --git a/src/error.rs b/src/error.rs index 3b3f573..360e62d 100644 --- a/src/error.rs +++ b/src/error.rs @@ -9,4 +9,10 @@ pub enum ObnamError { #[error("Generation has more than one file with the name {0}")] TooManyFiles(PathBuf), + + #[error("Server response did not have a 'chunk-meta' header for chunk {0}")] + NoChunkMeta(String), + + #[error("Wrong checksum for chunk {0}")] + WrongChecksum(String), } -- cgit v1.2.1