use std::collections::{HashSet, HashMap};
-use std::io::{self, Write, Seek, SeekFrom};
+use std::convert::TryFrom;
+use std::io::{self, Read, Write, Seek, SeekFrom};
+use std::os::unix::io::{FromRawFd, RawFd};
use std::path::{Path, PathBuf};
use std::pin::Pin;
use std::sync::{Arc, Mutex};
use proxmox_backup::pxar::catalog::*;
use proxmox_backup::backup::{
archive_type,
- load_and_decrypt_key,
+ decrypt_key,
verify_chunk_size,
ArchiveType,
AsyncReadChunk,
"Path to encryption key. All data will be encrypted using this key.")
.schema();
+pub const KEYFD_SCHEMA: Schema = IntegerSchema::new(
+ "Pass an encryption key via an already opened file descriptor.")
+ .minimum(0)
+ .schema();
+
const CHUNK_SIZE_SCHEMA: Schema = IntegerSchema::new(
"Chunk size in KB. Must be a power of 2.")
.minimum(64)
Ok((catalog, catalog_result_rx))
}
-fn keyfile_parameters(param: &Value) -> Result<(Option<PathBuf>, CryptMode), Error> {
+fn keyfile_parameters(param: &Value) -> Result<(Option<Vec<u8>>, CryptMode), Error> {
let keyfile = match param.get("keyfile") {
Some(Value::String(keyfile)) => Some(keyfile),
Some(_) => bail!("bad --keyfile parameter type"),
None => None,
};
+ let key_fd = match param.get("keyfd") {
+ Some(Value::Number(key_fd)) => Some(
+ RawFd::try_from(key_fd
+ .as_i64()
+ .ok_or_else(|| format_err!("bad key fd: {:?}", key_fd))?
+ )
+ .map_err(|err| format_err!("bad key fd: {:?}: {}", key_fd, err))?
+ ),
+ Some(_) => bail!("bad --keyfd parameter type"),
+ None => None,
+ };
+
let crypt_mode: Option<CryptMode> = match param.get("crypt-mode") {
Some(mode) => Some(serde_json::from_value(mode.clone())?),
None => None,
};
- Ok(match (keyfile, crypt_mode) {
+ let keydata = match (keyfile, key_fd) {
+ (None, None) => None,
+ (Some(_), Some(_)) => bail!("--keyfile and --keyfd are mutually exclusive"),
+ (Some(keyfile), None) => Some(file_get_contents(keyfile)?),
+ (None, Some(fd)) => {
+ let input = unsafe { std::fs::File::from_raw_fd(fd) };
+ let mut data = Vec::new();
+ let _len: usize = { input }.read_to_end(&mut data)
+ .map_err(|err| {
+ format_err!("error reading encryption key from fd {}: {}", fd, err)
+ })?;
+ Some(data)
+ }
+ };
+
+ Ok(match (keydata, crypt_mode) {
// no parameters:
- (None, None) => (key::optional_default_key_path()?, CryptMode::Encrypt),
+ (None, None) => match key::read_optional_default_encryption_key()? {
+ Some(key) => (Some(key), CryptMode::Encrypt),
+ None => (None, CryptMode::None),
+ },
// just --crypt-mode=none
(None, Some(CryptMode::None)) => (None, CryptMode::None),
// just --crypt-mode other than none
- (None, Some(crypt_mode)) => match key::optional_default_key_path()? {
+ (None, Some(crypt_mode)) => match key::read_optional_default_encryption_key()? {
None => bail!("--crypt-mode without --keyfile and no default key file available"),
- Some(path) => (Some(path), crypt_mode),
+ Some(key) => (Some(key), crypt_mode),
}
// just --keyfile
- (Some(keyfile), None) => (Some(PathBuf::from(keyfile)), CryptMode::Encrypt),
+ (Some(key), None) => (Some(key), CryptMode::Encrypt),
// --keyfile and --crypt-mode=none
(Some(_), Some(CryptMode::None)) => {
- bail!("--keyfile and --crypt-mode=none are mutually exclusive");
+ bail!("--keyfile/--keyfd and --crypt-mode=none are mutually exclusive");
}
// --keyfile and --crypt-mode other than none
- (Some(keyfile), Some(crypt_mode)) => (Some(PathBuf::from(keyfile)), crypt_mode),
+ (Some(key), Some(crypt_mode)) => (Some(key), crypt_mode),
})
}
schema: KEYFILE_SCHEMA,
optional: true,
},
+ "keyfd": {
+ schema: KEYFD_SCHEMA,
+ optional: true,
+ },
"crypt-mode": {
type: CryptMode,
optional: true,
verify_chunk_size(size)?;
}
- let (keyfile, crypt_mode) = keyfile_parameters(¶m)?;
+ let (keydata, crypt_mode) = keyfile_parameters(¶m)?;
let backup_id = param["backup-id"].as_str().unwrap_or(&proxmox::tools::nodename());
println!("Starting protocol: {}", start_time.to_rfc3339_opts(chrono::SecondsFormat::Secs, false));
- let (crypt_config, rsa_encrypted_key) = match keyfile {
+ let (crypt_config, rsa_encrypted_key) = match keydata {
None => (None, None),
- Some(path) => {
- let (key, created) = load_and_decrypt_key(&path, &key::get_encryption_key_password)?;
+ Some(key) => {
+ let (key, created) = decrypt_key(&key, &key::get_encryption_key_password)?;
let crypt_config = CryptConfig::new(key)?;
- let path = master_pubkey_path()?;
- if path.exists() {
- let pem_data = file_get_contents(&path)?;
- let rsa = openssl::rsa::Rsa::public_key_from_pem(&pem_data)?;
- let enc_key = crypt_config.generate_rsa_encoded_key(rsa, created)?;
- (Some(Arc::new(crypt_config)), Some(enc_key))
- } else {
- (Some(Arc::new(crypt_config)), None)
+ match key::find_master_pubkey()? {
+ Some(ref path) if path.exists() => {
+ let pem_data = file_get_contents(path)?;
+ let rsa = openssl::rsa::Rsa::public_key_from_pem(&pem_data)?;
+ let enc_key = crypt_config.generate_rsa_encoded_key(rsa, created)?;
+ (Some(Arc::new(crypt_config)), Some(enc_key))
+ }
+ _ => (Some(Arc::new(crypt_config)), None),
}
}
};
schema: KEYFILE_SCHEMA,
optional: true,
},
+ "keyfd": {
+ schema: KEYFD_SCHEMA,
+ optional: true,
+ },
"crypt-mode": {
type: CryptMode,
optional: true,
let target = tools::required_string_param(¶m, "target")?;
let target = if target == "-" { None } else { Some(target) };
- let (keyfile, _crypt_mode) = keyfile_parameters(¶m)?;
+ let (keydata, _crypt_mode) = keyfile_parameters(¶m)?;
- let crypt_config = match keyfile {
+ let crypt_config = match keydata {
None => None,
- Some(path) => {
- let (key, _) = load_and_decrypt_key(&path, &key::get_encryption_key_password)?;
+ Some(key) => {
+ let (key, _) = decrypt_key(&key, &key::get_encryption_key_password)?;
Some(Arc::new(CryptConfig::new(key)?))
}
};
schema: KEYFILE_SCHEMA,
optional: true,
},
+ "keyfd": {
+ schema: KEYFD_SCHEMA,
+ optional: true,
+ },
"crypt-mode": {
type: CryptMode,
optional: true,
let mut client = connect(repo.host(), repo.user())?;
- let (keyfile, crypt_mode) = keyfile_parameters(¶m)?;
+ let (keydata, crypt_mode) = keyfile_parameters(¶m)?;
- let crypt_config = match keyfile {
+ let crypt_config = match keydata {
None => None,
- Some(path) => {
- let (key, _created) = load_and_decrypt_key(&path, &key::get_encryption_key_password)?;
+ Some(key) => {
+ let (key, _created) = decrypt_key(&key, &key::get_encryption_key_password)?;
let crypt_config = CryptConfig::new(key)?;
Some(Arc::new(crypt_config))
}
result
}
-fn master_pubkey_path() -> Result<PathBuf, Error> {
- let base = BaseDirectories::with_prefix("proxmox-backup")?;
-
- // usually $HOME/.config/proxmox-backup/master-public.pem
- let path = base.place_config_file("master-public.pem")?;
-
- Ok(path)
-}
-
use proxmox_backup::client::RemoteChunkReader;
/// This is a workaround until we have cleaned up the chunk/reader/... infrastructure for better
/// async use!
buf: &'a mut [u8],
offset: u64,
) -> MaybeReady<io::Result<usize>, ReadAtOperation<'a>> {
- use std::io::Read;
MaybeReady::Ready(tokio::task::block_in_place(move || {
let mut reader = self.inner.lock().unwrap();
reader.seek(SeekFrom::Start(offset))?;