Skip to content

Commit

Permalink
fix: race condition in private key creation (#4673)
Browse files Browse the repository at this point in the history
  • Loading branch information
LesnyRumcajs authored Aug 22, 2024
1 parent b9c5d42 commit d0ba0d9
Show file tree
Hide file tree
Showing 6 changed files with 39 additions and 53 deletions.
1 change: 0 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,6 @@ jsonrpsee = { version = "0.24", features = ["server", "ws-client", "http-client"
jsonwebtoken = "9"
keccak-hash = "0.10"
kubert-prometheus-process = "0.1"
libc = "0.2"
libipld = { version = "0.16", default-features = false, features = ["dag-cbor", "dag-json", "derive", "serde-codec"] }
libipld-core = { version = "0.16", features = ['arb', 'serde-codec'] }
libipld-macro = "0.16"
Expand Down
18 changes: 6 additions & 12 deletions src/key_management/keystore.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@

use std::{
fmt::Display,
fs::{self, create_dir, File},
fs::{create_dir, File},
io::{BufReader, BufWriter, ErrorKind, Read, Write},
path::{Path, PathBuf},
};

use crate::{shim::crypto::SignatureType, utils::encoding::from_slice_with_fallback};
use crate::{
shim::crypto::SignatureType,
utils::{encoding::from_slice_with_fallback, io::create_new_sensitive_file},
};
use ahash::{HashMap, HashMapExt};
use argon2::{
password_hash::SaltString, Argon2, ParamsBuilder, PasswordHasher, RECOMMENDED_SALT_LEN,
Expand Down Expand Up @@ -269,16 +272,7 @@ impl KeyStore {
pub fn flush(&self) -> anyhow::Result<()> {
match &self.persistence {
Some(persistent_keystore) => {
let dir = persistent_keystore
.file_path
.parent()
.ok_or_else(|| Error::Other("Invalid Path".to_string()))?;
fs::create_dir_all(dir)?;
let file = File::create(&persistent_keystore.file_path)?;

// Restrict permissions on files containing private keys
#[cfg(unix)]
crate::utils::io::set_user_perm(&file)?;
let file = create_new_sensitive_file(&persistent_keystore.file_path)?;

let mut writer = BufWriter::new(file);

Expand Down
8 changes: 3 additions & 5 deletions src/libp2p/keypair.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

use tracing::{debug, info, trace};

use crate::{libp2p::Keypair, utils::io::write_to_file};
use crate::{libp2p::Keypair, utils::io::write_new_sensitive_file};
use std::{fs, path::Path};

const KEYPAIR_FILE: &str = "keypair";
Expand All @@ -29,12 +29,10 @@ fn create_and_save_keypair(path: &Path) -> anyhow::Result<Keypair> {
backup_path.set_extension("bak");

info!("Backing up existing keypair to {}", backup_path.display());
fs::rename(keypair_path, &backup_path)?;
fs::rename(&keypair_path, &backup_path)?;
}

let file = write_to_file(&gen_keypair.to_bytes(), path, KEYPAIR_FILE)?;
// Restrict permissions on files containing private keys
crate::utils::io::set_user_perm(&file)?;
write_new_sensitive_file(&gen_keypair.to_bytes(), &keypair_path)?;

Ok(gen_keypair.into())
}
Expand Down
54 changes: 25 additions & 29 deletions src/utils/io/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,45 +6,41 @@ pub mod progress_log;
mod writer_checksum;

use std::{
fs::{create_dir_all, File},
io::{prelude::*, Result},
fs::File,
io::{self, prelude::*, Result},
os::unix::fs::OpenOptionsExt,
path::Path,
};

pub use mmap::EitherMmapOrRandomAccessFile;
pub use progress_log::{WithProgress, WithProgressRaw};
pub use writer_checksum::*;

/// Restricts permissions on a file to user-only: 0600
#[cfg(unix)]
pub fn set_user_perm(file: &File) -> Result<()> {
use std::os::unix::fs::PermissionsExt;

use tracing::info;

let mut perm = file.metadata()?.permissions();
#[allow(clippy::useless_conversion)] // Otherwise it does not build on macos
perm.set_mode((libc::S_IWUSR | libc::S_IRUSR).into());
file.set_permissions(perm)?;

info!("Permissions set to 0600 on {:?}", file);

Ok(())
/// Writes bytes to a specified file. Creates the desired path if it does not
/// exist.
/// Note: The file is created with permissions 0600.
/// Note: The file is truncated if it already exists.
pub fn write_new_sensitive_file(message: &[u8], path: &Path) -> Result<()> {
create_new_sensitive_file(path)?.write_all(message)
}

#[cfg(not(unix))]
pub fn set_user_perm(file: &File) -> Result<()> {
Ok(())
}
/// Creates a new file with the specified path. The file is created
/// with permissions 0600 and is truncated if it already exists.
pub fn create_new_sensitive_file(path: &Path) -> Result<File> {
std::fs::create_dir_all(
path.parent()
.ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Parent directory not found"))?,
)?;

/// Writes a string to a specified file. Creates the desired path if it does not
/// exist. Note: `path` and `filename` are appended to produce the resulting
/// file path.
pub fn write_to_file(message: &[u8], path: &Path, file_name: &str) -> Result<File> {
// Create path if it doesn't exist
create_dir_all(path)?;
let mut file = File::create(path.join(file_name))?;
file.write_all(message)?;
let file = std::fs::OpenOptions::new()
.mode(0o600)
.write(true)
.create(true)
.truncate(true)
.open(path)?;

use std::os::unix::fs::PermissionsExt;
file.set_permissions(std::fs::Permissions::from_mode(0o600))?;
Ok(file)
}

Expand Down
10 changes: 5 additions & 5 deletions src/utils/tests/files.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
path::{Path, PathBuf},
};

use crate::utils::io::{read_toml, write_to_file};
use crate::utils::io::{read_toml, write_new_sensitive_file};
use serde::Deserialize;

// Please use with caution, remove_dir_all will completely delete a directory
Expand All @@ -20,7 +20,7 @@ fn write_to_file_to_path() {
let path = PathBuf::from("./test-write");
let file = "test.txt";

match write_to_file(msg, &path, file) {
match write_new_sensitive_file(msg, &path.join(file)) {
Ok(_) => cleanup_file(&path),
Err(e) => {
cleanup_file(&path);
Expand All @@ -34,7 +34,7 @@ fn write_to_file_nested_dir() {
let msg = "Hello World!".as_bytes();
let root = PathBuf::from("./test_missing");

match write_to_file(msg, &root.join("test_write_string"), "test-file") {
match write_new_sensitive_file(msg, &root.join("test_write_string").join("test-file")) {
Ok(_) => cleanup_file(&root),
Err(e) => {
cleanup_file(&root);
Expand All @@ -48,7 +48,7 @@ fn read_from_file_vec() {
let msg = "Hello World!".as_bytes();
let path = PathBuf::from("./test_read_file");
let file_name = "out.keystore";
write_to_file(msg, &path, file_name).unwrap();
write_new_sensitive_file(msg, &path.join(file_name)).unwrap();

match std::fs::read(path.join(file_name)) {
Ok(contents) => {
Expand All @@ -68,7 +68,7 @@ fn read_from_file_string() {
let path = PathBuf::from("./test_string_read_file");
let file_name = "out.keystore";

write_to_file(msg.as_bytes(), &path, file_name).unwrap();
write_new_sensitive_file(msg.as_bytes(), &path.join(file_name)).unwrap();
match std::fs::read_to_string(path.join(file_name)) {
Ok(contents) => {
cleanup_file(&path);
Expand Down

0 comments on commit d0ba0d9

Please sign in to comment.