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 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
use crate::common::OpType;
use crate::{handle_client_error, PwBadlistOpt};
// use std::thread;
use std::fs::File;
use std::io::Read;
use tokio::task;
const CHUNK_SIZE: usize = 1000;
impl PwBadlistOpt {
pub fn debug(&self) -> bool {
match self {
PwBadlistOpt::Show(copt) => copt.debug,
PwBadlistOpt::Upload { copt, .. } => copt.debug,
PwBadlistOpt::Remove { copt, .. } => copt.debug,
}
}
pub async fn exec(&self) {
match self {
PwBadlistOpt::Show(copt) => {
let client = copt.to_client(OpType::Read).await;
match client.system_password_badlist_get().await {
Ok(list) => {
for i in list {
println!("{}", i);
}
eprintln!("--");
eprintln!("Success");
}
Err(e) => crate::handle_client_error(e, copt.output_mode),
}
}
PwBadlistOpt::Upload {
copt,
paths,
dryrun,
} => {
info!("pre-processing - this may take a while ...");
let mut pwset: Vec<String> = Vec::new();
for f in paths.iter() {
let mut file = match File::open(f) {
Ok(v) => v,
Err(e) => {
debug!(?e);
info!("Skipping file -> {:?}", f);
continue;
}
};
let mut contents = String::new();
if let Err(e) = file.read_to_string(&mut contents) {
error!("{:?} -> {:?}", f, e);
continue;
}
let mut inner_pw: Vec<_> =
contents.as_str().lines().map(str::to_string).collect();
pwset.append(&mut inner_pw);
}
debug!("Deduplicating pre-set ...");
pwset.sort_unstable();
pwset.dedup();
info!("Have {} unique passwords to process", pwset.len());
// Break the list into chunks per thread availability
// let par_count = thread::available_parallelism()
// .expect("Failed to determine available parallelism")
// .get();
let task_handles: Vec<_> = pwset
.chunks(CHUNK_SIZE)
.map(|chunk| chunk.to_vec())
.map(|chunk| {
task::spawn_blocking(move || {
let x = chunk
.iter()
.filter(|v| {
if v.len() < 10 {
return false;
}
match zxcvbn::zxcvbn(v.as_str(), &[]) {
Ok(r) => r.score() >= 4,
Err(e) => {
error!(
"zxcvbn unable to process '{}' - {:?}",
v.as_str(),
e
);
error!("adding to badlist anyway ...");
true
}
}
})
.map(|s| s.to_string())
.collect::<Vec<_>>();
eprint!(".");
x
})
})
.collect();
let mut filt_pwset = Vec::with_capacity(pwset.len());
for task_handle in task_handles {
let Ok(mut results) = task_handle.await else {
error!("Failed to join a worker thread, unable to proceed");
return;
};
filt_pwset.append(&mut results);
}
filt_pwset.sort_unstable();
info!(
"{} passwords passed zxcvbn, uploading ...",
filt_pwset.len()
);
if *dryrun {
for pw in filt_pwset {
println!("{}", pw);
}
} else {
let client = copt.to_client(OpType::Write).await;
match client.system_password_badlist_append(filt_pwset).await {
Ok(_) => println!("Success"),
Err(e) => handle_client_error(e, copt.output_mode),
}
}
} // End Upload
PwBadlistOpt::Remove { copt, paths } => {
let client = copt.to_client(OpType::Write).await;
let mut pwset: Vec<String> = Vec::new();
for f in paths.iter() {
let mut file = match File::open(f) {
Ok(v) => v,
Err(e) => {
debug!(?e);
info!("Skipping file -> {:?}", f);
continue;
}
};
let mut contents = String::new();
if let Err(e) = file.read_to_string(&mut contents) {
error!("{:?} -> {:?}", f, e);
continue;
}
let mut inner_pw: Vec<_> =
contents.as_str().lines().map(str::to_string).collect();
pwset.append(&mut inner_pw);
}
debug!("Deduplicating pre-set ...");
pwset.sort_unstable();
pwset.dedup();
if pwset.is_empty() {
eprintln!("No entries to remove?");
return;
}
match client.system_password_badlist_remove(pwset).await {
Ok(_) => println!("Success"),
Err(e) => handle_client_error(e, copt.output_mode),
}
} // End Remove
}
}
}