1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
use std::{env::temp_dir, fs::{self, File}, io::{BufReader, Read}, os::unix::fs::PermissionsExt, path::PathBuf};
use anyhow::bail;
use clap::Parser;
use getset::Getters;
use sha1::{Sha1, Digest};
use super::Commands;
#[derive(Parser, Getters)]
#[getset(get = "pub")]
#[clap(name="ntdsextract2", author, version, about, long_about = None)]
pub struct Args {
#[clap(subcommand)]
pub(crate) command: Commands,
/// name of the file to analyze
pub(crate) ntds_file: String,
#[clap(flatten)]
pub(crate) verbose: clap_verbosity_flag::Verbosity,
#[clap(long = "use-cache", default_value_t = false)]
/// set this flag if you want to use a cache file, to speed up you work
/// with ntdsextract2
pub(crate) use_cache: bool,
/// cache file to use. If you use the cache, but don't specify a filename,
/// ntdsextract2 will generate a filename and store a cache in /tmp
///
/// Be aware that ntdsextract2 will overwrite this file at any time without
/// any warning!!!
#[clap(long = "cache-file")]
pub(crate) cache_file: Option<String>,
}
impl Args {
pub fn find_cache_file(&self) -> Result<Option<PathBuf>, anyhow::Error> {
Ok(if *self.use_cache() {
if let Some(cache_file) = self.cache_file() {
let cache_file = PathBuf::from(cache_file).canonicalize()?;
if !cache_file.exists() {
// the cache file does not exist, so we need to create one.
// Therefore, the parent directory must be writable
if let Some(parent) = cache_file.parent() {
let permissions = fs::metadata(parent)?.permissions();
if permissions.readonly() {
bail!(
"directory '{}' must be writable in order to store a cache file there",
parent.to_string_lossy()
);
}
let permissions = fs::metadata(&cache_file)?.permissions();
if permissions.mode() & 0o177 != 0 {
bail!(
"cache file '{}' has too lose permissions set",
cache_file.to_string_lossy()
);
}
Some(cache_file)
} else {
bail!("invalid path: '{}'", cache_file.to_string_lossy());
}
} else {
if !cache_file.is_file() {
bail!(
"Path '{}' does not point to a file",
cache_file.to_string_lossy()
);
}
Some(cache_file)
}
} else {
// read the first 4k bytes from the database to create a simple fingerprint
let buffer = {
let mut reader = BufReader::new(File::open(self.ntds_file()).unwrap());
let mut buffer = [0; 4096];
let _ = reader.read(&mut buffer)?;
buffer
};
let hash = {
let mut hasher = Sha1::new();
hasher.update(buffer);
let result = hasher.finalize();
hex::encode(&result[0..8])
};
let mut cache_file = temp_dir();
cache_file.push(format!("ntdsextract2_cache_{}", hash));
if cache_file.exists() {
let permissions = fs::metadata(&cache_file)?.permissions();
if permissions.mode() & 0o177 != 0 {
bail!(
"cache file '{}' has too loose permissions set: {:o}",
cache_file.to_string_lossy(),
permissions.mode()
);
}
}
Some(cache_file)
}
} else {
None
})
}
}