v0.25.5
This commit is contained in:
1 parent
1dea7f55c8
commit
c819f0a335
151 files changed
+7610
-4264
No files matched your search
+64
-21
@@ -29,7 +29,10 @@ fn backup_input(path: &Path) -> Result<File> {
|
||||
options.custom_flags(libc::O_NOFOLLOW);
|
||||
}
|
||||
let file = options.open(path)?;
|
||||
ensure!(file.metadata()?.is_file(), "backup input must be a regular file");
|
||||
ensure!(
|
||||
file.metadata()?.is_file(),
|
||||
"backup input must be a regular file"
|
||||
);
|
||||
Ok(file)
|
||||
}
|
||||
|
||||
@@ -65,7 +68,9 @@ fn copy_snapshot_file(source: &Path, target: &Path) -> Result<(u64, String)> {
|
||||
}
|
||||
output.write_all(&buffer[..read])?;
|
||||
hasher.update(&buffer[..read]);
|
||||
size = size.checked_add(read as u64).context("backup input too large")?;
|
||||
size = size
|
||||
.checked_add(read as u64)
|
||||
.context("backup input too large")?;
|
||||
}
|
||||
output.sync_all()?;
|
||||
let after = input.metadata()?;
|
||||
@@ -82,7 +87,10 @@ fn symlink_target_bytes(target: &Path) -> Result<Vec<u8>> {
|
||||
{
|
||||
use std::os::unix::ffi::OsStrExt;
|
||||
let bytes = target.as_os_str().as_bytes().to_vec();
|
||||
ensure!(!bytes.is_empty() && bytes.len() <= 4096, "invalid symlink target");
|
||||
ensure!(
|
||||
!bytes.is_empty() && bytes.len() <= 4096,
|
||||
"invalid symlink target"
|
||||
);
|
||||
return Ok(bytes);
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
@@ -92,13 +100,19 @@ fn symlink_target_bytes(target: &Path) -> Result<Vec<u8>> {
|
||||
.context("symlink target must be valid UTF-8")?
|
||||
.as_bytes()
|
||||
.to_vec();
|
||||
ensure!(!bytes.is_empty() && bytes.len() <= 4096, "invalid symlink target");
|
||||
ensure!(
|
||||
!bytes.is_empty() && bytes.len() <= 4096,
|
||||
"invalid symlink target"
|
||||
);
|
||||
Ok(bytes)
|
||||
}
|
||||
}
|
||||
|
||||
fn symlink_target_path(bytes: &[u8]) -> Result<PathBuf> {
|
||||
ensure!(!bytes.is_empty() && bytes.len() <= 4096, "invalid symlink target");
|
||||
ensure!(
|
||||
!bytes.is_empty() && bytes.len() <= 4096,
|
||||
"invalid symlink target"
|
||||
);
|
||||
#[cfg(unix)]
|
||||
{
|
||||
use std::os::unix::ffi::OsStringExt;
|
||||
@@ -106,22 +120,33 @@ fn symlink_target_path(bytes: &[u8]) -> Result<PathBuf> {
|
||||
}
|
||||
#[cfg(not(unix))]
|
||||
{
|
||||
Ok(PathBuf::from(std::str::from_utf8(bytes).context("symlink target is not UTF-8")?))
|
||||
Ok(PathBuf::from(
|
||||
std::str::from_utf8(bytes).context("symlink target is not UTF-8")?,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn validate_symlink_target(link_relative: &Path, target: &Path) -> Result<()> {
|
||||
ensure!(!target.is_absolute(), "backup symlink target must be relative");
|
||||
let mut depth = link_relative.parent().map_or(0usize, |parent| parent.components().count());
|
||||
ensure!(
|
||||
!target.is_absolute(),
|
||||
"backup symlink target must be relative"
|
||||
);
|
||||
let mut depth = link_relative
|
||||
.parent()
|
||||
.map_or(0usize, |parent| parent.components().count());
|
||||
for component in target.components() {
|
||||
match component {
|
||||
Component::Normal(_) => depth = depth.checked_add(1).context("symlink target too deep")?,
|
||||
Component::Normal(_) => {
|
||||
depth = depth.checked_add(1).context("symlink target too deep")?
|
||||
}
|
||||
Component::CurDir => {}
|
||||
Component::ParentDir => {
|
||||
ensure!(depth > 0, "backup symlink escapes the data directory");
|
||||
depth -= 1;
|
||||
}
|
||||
Component::RootDir | Component::Prefix(_) => bail!("backup symlink target must be relative"),
|
||||
Component::RootDir | Component::Prefix(_) => {
|
||||
bail!("backup symlink target must be relative")
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -152,7 +177,10 @@ fn collect_data_files(
|
||||
let path = item.path();
|
||||
let name = item.file_name();
|
||||
let name_text = name.to_string_lossy();
|
||||
if excluded_file(&name_text) || excluded_directory(&name_text) || path == root.join("secrets") {
|
||||
if excluded_file(&name_text)
|
||||
|| excluded_directory(&name_text)
|
||||
|| path == root.join("secrets")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
let meta = fs::symlink_metadata(&path)?;
|
||||
@@ -215,30 +243,45 @@ fn validate_snapshot_symlinks(snapshot_root: &Path, symlinks: &[PathBuf]) -> Res
|
||||
for link in symlinks {
|
||||
let resolved = fs::canonicalize(link)
|
||||
.with_context(|| format!("backup symlink target is missing: {}", link.display()))?;
|
||||
ensure!(resolved.starts_with(&root), "backup symlink escapes the data directory: {}", link.display());
|
||||
ensure!(
|
||||
resolved.starts_with(&root),
|
||||
"backup symlink escapes the data directory: {}",
|
||||
link.display()
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_snapshot_config(envelope: &crate::config::Envelope, snapshot_root: &Path) -> Result<()> {
|
||||
fn validate_snapshot_config(
|
||||
envelope: &crate::config::Envelope,
|
||||
snapshot_root: &Path,
|
||||
) -> Result<()> {
|
||||
let cert_dir = snapshot_root.join("certs");
|
||||
for certificate in &envelope.config.tls.certificates {
|
||||
for name in [&certificate.cert_file, &certificate.key_file] {
|
||||
crate::tls::safe_name(name)?;
|
||||
let path = cert_dir.join(name);
|
||||
let meta = fs::symlink_metadata(&path)
|
||||
.with_context(|| format!("configured certificate file changed during backup: {name}"))?;
|
||||
ensure!(meta.is_file() && !meta.file_type().is_symlink(), "configured certificate file is not a regular file: {name}");
|
||||
let meta = fs::symlink_metadata(&path).with_context(|| {
|
||||
format!("configured certificate file changed during backup: {name}")
|
||||
})?;
|
||||
ensure!(
|
||||
meta.is_file() && !meta.file_type().is_symlink(),
|
||||
"configured certificate file is not a regular file: {name}"
|
||||
);
|
||||
}
|
||||
}
|
||||
if envelope.config.administration.tls && envelope.config.administration.certificate_id.is_none() {
|
||||
if envelope.config.administration.tls && envelope.config.administration.certificate_id.is_none()
|
||||
{
|
||||
for name in ["default.pem", "default.key"] {
|
||||
let path = cert_dir.join(name);
|
||||
let meta = fs::symlink_metadata(&path)
|
||||
.with_context(|| format!("default administration certificate changed during backup: {name}"))?;
|
||||
ensure!(meta.is_file() && !meta.file_type().is_symlink(), "default administration certificate is not a regular file: {name}");
|
||||
let meta = fs::symlink_metadata(&path).with_context(|| {
|
||||
format!("default administration certificate changed during backup: {name}")
|
||||
})?;
|
||||
ensure!(
|
||||
meta.is_file() && !meta.file_type().is_symlink(),
|
||||
"default administration certificate is not a regular file: {name}"
|
||||
);
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user