use base32::*;
use base64::prelude::*;
use chrono::Local;
use hmac::{Hmac, Mac};
use kpdb::{CompositeKey, Database, Entry};
use pbkdf2::pbkdf2_hmac;
use pwhash::*;
use rand::distributions::Alphanumeric;
use rand::thread_rng;
use rand::Rng;
use sha1::{Digest, Sha1};
use sha2::Sha256;
use sha2::Sha512;
use std::fs::File;
use std::io::BufRead;
use std::io::BufReader;
use std::thread;
use std::time::Duration;
use std::time::SystemTime;
use std::time::UNIX_EPOCH;
#[derive(Clone)]
pub enum TotpAlgo {
Sha1,
Sha256,
Sha512,
}
#[derive(Clone)]
pub struct Config {
pub number: Option<u32>,
pub len: Option<u32>,
pub pw_type: Option<u32>,
pub word_list: Option<String>,
pub words: Option<Vec<String>>,
pub username: Option<String>,
pub database: Option<String>,
pub create_database: bool,
pub spaces: bool,
pub digest: Option<String>,
pub servername: Option<String>,
pub keepassdb: Option<String>,
pub keepassfetch: bool,
pub keepassphrase: Option<String>,
pub totp_key: Option<String>,
pub totp_step: Option<u64>,
pub totp_algo: TotpAlgo,
pub totp_seconds: Option<u64>,
pub password: Option<String>,
pub loopdelay: Option<u32>,
pub salt: Option<String>,
pub wpinst_fmt: bool,
pub wpuser_fmt: bool,
}
impl Config {
pub fn new() -> Config {
Config {
len: None,
pw_type: None,
number: None,
word_list: None,
words: None,
username: None,
database: None,
create_database: false,
spaces: true,
digest: None,
servername: None,
keepassdb: None,
keepassfetch: false,
keepassphrase: None,
totp_key: None,
totp_step: Some(30),
totp_seconds: None,
password: None,
totp_algo: TotpAlgo::Sha1,
loopdelay: None,
wpinst_fmt: false,
wpuser_fmt: false,
salt: None,
}
}
pub fn digest(&self) -> String {
if self.digest.is_none() {
return "sha256".to_string();
}
self.digest.as_ref().unwrap().to_string()
}
pub fn totp_seconds(&self) -> u64 {
if self.totp_seconds.is_some() {
self.totp_seconds.unwrap()
} else {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs()
}
}
}
impl Default for Config {
fn default() -> Self {
Self::new()
}
}
pub enum PwClass {
Num = 1 << 0,
Alpha = 1 << 1,
Ext = 1 << 2,
Lower = 1 << 3,
Upper = 1 << 4,
}
pub fn valid_word(s: &str, word_set: Option<u32>) -> bool {
if let Some(word_set) = word_set {
let mut valid = false;
if word_set & PwClass::Num as u32 != 0 {
for j in s.chars() {
if j.is_ascii_digit() {
valid = true;
}
}
}
if word_set & PwClass::Lower as u32 != 0 {
for j in s.chars() {
if j.is_ascii_lowercase() {
valid = true;
}
}
}
if word_set & PwClass::Upper as u32 != 0 {
for j in s.chars() {
if j.is_ascii_uppercase() {
valid = true;
}
}
}
if word_set & PwClass::Ext as u32 != 0 {
for j in s.chars() {
if !j.is_ascii_digit() && !j.is_ascii_lowercase() && !j.is_ascii_uppercase() {
valid = true;
}
}
}
return valid;
};
true
}
pub fn prng_string(c: &Config) -> String {
let mut rng = rand::thread_rng();
if c.word_list.is_some() {
let mut phrase = vec![];
let words_len = c.words.as_ref().unwrap().len();
if c.words.as_ref().unwrap().is_empty() {
eprintln!("no words to process");
std::process::exit(1);
}
for _j in 0..c.len.unwrap_or(15) {
phrase.push(
c.words.as_ref().unwrap()[rng.gen_range(0..words_len)]
.clone()
.to_string(),
);
}
let mut phrase = phrase.join(if c.spaces { " " } else { "" });
if c.pw_type.is_some() {
let set = c.pw_type.as_ref().unwrap();
if set & PwClass::Lower as u32 != 0 {
phrase = phrase.to_lowercase();
}
if set & PwClass::Upper as u32 != 0 {
phrase = phrase.to_uppercase();
}
}
return phrase;
}
let mut set = c.pw_type;
if set.is_none() {
set = Some(
PwClass::Num as u32
| PwClass::Alpha as u32
| PwClass::Lower as u32
| PwClass::Upper as u32,
);
}
let set = set.unwrap();
let mut chars = "".to_string();
if set & PwClass::Num as u32 != 0 {
chars += "0123456789";
};
if set & PwClass::Alpha as u32 != 0 {
chars += "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
};
if set & PwClass::Lower as u32 != 0 {
chars += "abcdefghijklmnopqrstuvwxyz";
};
if set & PwClass::Upper as u32 != 0 {
chars += "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
};
if set & PwClass::Ext as u32 != 0 {
chars += r#"!"$%^&*()-={}[]:;@'<>,./\|"#;
};
let one_char = || chars.chars().nth(rng.gen_range(0..chars.len())).unwrap();
std::iter::repeat_with(one_char)
.take(c.len.unwrap_or(15) as usize)
.collect()
}
pub fn gen_salt_str(chars: usize) -> String {
let rng = thread_rng();
rng.sample_iter(&Alphanumeric)
.take(chars)
.map(|x| x as char)
.collect()
}
pub fn postgres_pass(pw: &str) -> String {
let salt_size = 16;
let iterations = 4096;
let salt = gen_salt_str(salt_size);
let mut key = [0u8; 32];
pbkdf2_hmac::<Sha256>(pw.as_bytes(), salt.as_bytes(), iterations, &mut key);
let mut client_key = Hmac::<Sha256>::new_from_slice(&key).unwrap();
client_key.update(b"Client Key");
let client_result = client_key.finalize();
let mut server_key = Hmac::<Sha256>::new_from_slice(&key).unwrap();
server_key.update(b"Server Key");
let server_result = server_key.finalize();
let mut stored_key = Sha256::new();
stored_key.update(client_result.clone().into_bytes());
let stored_key = stored_key.finalize();
let bstored_key = BASE64_STANDARD.encode(stored_key);
let bsalt = BASE64_STANDARD.encode(salt);
let bserver_key = BASE64_STANDARD.encode(server_result.clone().into_bytes());
format!("SCRAM-SHA-256${iterations}:{bsalt}${bstored_key}:{bserver_key}")
}
pub fn mysql_pass(pw: &str) -> String {
let mut h = Sha1::new();
h.update(pw);
let r = h.finalize();
let mut h = Sha1::new();
h.update(r);
let r = h.finalize();
format!("*{:x}", r).to_string()
}
pub fn to64(mut v: i32, n: i32) -> String {
let mut index64: Vec<u8> = vec![];
index64.push(b'.');
index64.push(b'/');
for i in 48..58 {
index64.push(i);
}
for i in 65..91 {
index64.push(i);
}
for i in 97..123 {
index64.push(i);
}
let mut result = "".to_string(); let mut nn = n;
while nn > 0 {
nn -= 1;
result.push(index64[v as usize & 0x3f] as char);
v >>= 6;
}
String::from_utf8_lossy(result.as_bytes()).to_string()
}
pub fn mysql_caching_sha2_pass_salt(pw: &str, salt: &str, iterations: i32) -> String {
let num_bytes = 32;
let key = pw.as_bytes();
let salt = salt.as_bytes();
let mut d1: Vec<u8> = vec![];
d1.extend(key);
d1.extend(salt);
d1.extend(key);
let mut stored_key = Sha256::new();
stored_key.update(d1);
let digest_b = stored_key.clone().finalize();
let mut tmp: Vec<u8> = vec![];
tmp.extend(key);
tmp.extend(salt);
for i in (0..=key.len()).rev().step_by(num_bytes) {
tmp.extend(if i > num_bytes {
digest_b.as_slice()
} else {
&digest_b[0..i]
});
}
let mut i = key.len();
while i > 0 {
tmp.extend(if i & 1 != 0 {
digest_b.as_slice()
} else {
key as &[u8]
});
i >>= 1;
}
let mut digest_a_hash = Sha256::new();
digest_a_hash.update(tmp);
let digest_a = digest_a_hash.clone().finalize();
let mut tmp: Vec<u8> = vec![];
for _ in 0..key.len() {
tmp.extend(key);
}
let mut digest_dp_hash = Sha256::new();
digest_dp_hash.update(tmp);
let digest_dp = digest_dp_hash.finalize();
let mut byte_sequence_p: Vec<u8> = vec![];
for i in (0..=key.len()).rev().step_by(num_bytes) {
byte_sequence_p.extend(if i > num_bytes {
digest_dp.as_slice()
} else {
&digest_dp[0..i]
});
}
let mut tmp: Vec<u8> = vec![];
let til = 16 + (digest_a[0] as i32);
for _ in 0..til {
tmp.extend(salt);
}
let mut digest_ds_hash = Sha256::new();
digest_ds_hash.update(tmp);
let digest_ds = digest_ds_hash.finalize();
let mut byte_sequence_s: Vec<u8> = vec![];
for i in (0..=salt.len()).rev().step_by(num_bytes) {
byte_sequence_s.extend(if i > num_bytes {
digest_ds.as_slice()
} else {
&digest_ds[0..i]
});
}
let mut digest_c = digest_a;
for i in 0..iterations * 1000 {
tmp = if i & 1 > 0 {
byte_sequence_p.clone()
} else {
(&digest_c as &[u8]).to_vec()
};
if i % 3 > 0 {
tmp.extend(byte_sequence_s.clone());
}
if i % 7 > 0 {
tmp.extend(byte_sequence_p.clone());
}
tmp.extend(if i & 1 > 0 {
digest_c.as_slice()
} else {
&byte_sequence_p as &[u8]
});
let mut digest_c_hash = Sha256::new();
digest_c_hash.update(tmp);
digest_c = digest_c_hash.finalize();
}
let inc1 = 10;
let inc2 = 21;
let m = 30;
let end = 0;
let mut i = 0;
let mut tmp = "".to_string();
loop {
tmp.push_str(&to64(
((digest_c[i] as i32) << 16)
| (((digest_c[(i + inc1) % m]) as i32) << 8)
| (digest_c[(i + inc1 * 2) % m]) as i32,
4,
));
i = (i + inc2) % m;
if i == end {
break;
}
}
tmp.push_str(&to64(
((digest_c[31] as i32) << 8) | (digest_c[30] as i32),
3,
));
tmp
}
pub fn mysql_caching_sha2_pass(pw: &str) -> String {
let salt_size = 20;
let salt = gen_salt_str(salt_size);
let count = 5;
let st = mysql_caching_sha2_pass_salt(pw, &salt, count);
format!(
"0x{}",
hex::encode(format!("$A${count:>03}${salt}{st}")).to_uppercase()
)
}
pub fn useradd_format(c: &Config) -> String {
let digest = c.digest();
format!("useradd -m -s /bin/bash -p '%{{{digest}}}' %{{username}}").to_string()
}
pub fn usermod_format(c: &Config) -> String {
let digest = c.digest();
format!("usermod -p '%{{{digest}}}' %{{username}}").to_string()
}
pub fn mysql_format() -> String {
"grant all privileges on %{database}.* to %{username}@'%' identified with mysql_native_password as '%{mysql}';".to_string()
}
pub fn mysql_user_format() -> String {
"create user %{username}@'%'; alter user %{username}@'%' identified with 'caching_sha2_password' as %{mysql_caching_sha2}; grant all privileges on %{database}.* to %{username}@'%';".to_string()
}
pub fn postgres_format() -> String {
"create user %{username} password '%{postgres}';".to_string()
}
pub fn htauth_format(c: &Config) -> String {
let digest = if c.digest.is_none() {
"md5".to_string()
} else {
c.digest()
};
format!("%{{username}}:%{{{digest}}}").to_string()
}
pub fn wpinst_format() -> String {
"wp core install --url=https://%{servername}/ --title=%{servername} --admin_email=%{username}@%{servername} --admin_user=%{username} --admin_password=%{password} --skip-email".to_string()
}
pub fn wpuser_format() -> String {
"update wp_users set user_pass = '%{bcrypt}' where user_login = '%{username}'".to_string()
}
pub fn wpconfig_format() -> String {
"wp config create --dbname=%{database} --dbuser=%{username} --dbpass=%{password}".to_string()
}
fn test_crypt_result(a: &Result<String>, c: &Config) -> String {
match a {
Ok(x) => x.to_string(),
Err(x) => {
eprintln!("Invalid crypt {}: {}", c.salt.as_ref().unwrap(), x);
std::process::exit(1);
}
}
}
fn base32_format(pw: String) -> String {
base32::encode(Alphabet::Rfc4648Lower { padding: false }, &pw.into_bytes())
}
#[allow(deprecated)]
pub fn process_format_string(format_string: &mut String, c: &Config, pw: &str) -> String {
if format_string.contains("%{userfmt}") {
*format_string = format_string.replace("%{userfmt}", &useradd_format(c).to_string());
}
if format_string.contains("%{usermodfmt}") {
*format_string = format_string.replace("%{usermodfmt}", &usermod_format(c).to_string());
}
if format_string.contains("%{mysqlfmt}") {
*format_string = format_string.replace("%{mysqlfmt}", &mysql_format().to_string());
}
if format_string.contains("%{mysqluserfmt}") {
*format_string = format_string.replace("%{mysqluserfmt}", &mysql_user_format().to_string());
}
if format_string.contains("%{pgfmt}") {
*format_string = format_string.replace("%{pgfmt}", &postgres_format().to_string());
}
if format_string.contains("%{htauthfmt}") {
*format_string = format_string.replace("%{htauthfmt}", &htauth_format(c).to_string());
}
if format_string.contains("%{wpconfigfmt}") {
*format_string = format_string.replace("%{wpconfigfmt}", &wpconfig_format().to_string());
}
if format_string.contains("%{wpuserfmt}") {
*format_string = format_string.replace("%{wpuserfmt}", &wpuser_format().to_string());
}
if format_string.contains("%{wpinstfmt}") {
*format_string = format_string.replace("%{wpinstfmt}", &wpinst_format().to_string());
}
if format_string.contains("%{totpfmt}") {
*format_string = format_string.replace(
"%{totpfmt}",
&("%{totp} [%{totpprogress}]".to_owned()
+ if c.username.is_some() {
" %{username}"
} else {
""
}),
);
}
if format_string.contains("%{base32}") {
*format_string =
format_string.replace("%{base32}", &base32_format(pw.to_string()).to_string());
}
if format_string.contains("%{md5}") {
let crypt = if c.salt.is_some() {
test_crypt_result(
&md5_crypt::hash_with(c.salt.as_ref().unwrap().as_str(), pw),
c,
)
} else {
md5_crypt::hash(pw).unwrap().to_string()
};
*format_string = format_string.replace("%{md5}", &crypt);
}
if format_string.contains("%{bcrypt}") {
let crypt = if c.salt.is_some() {
test_crypt_result(&bcrypt::hash_with(c.salt.as_ref().unwrap().as_str(), pw), c)
} else {
bcrypt::hash(pw).unwrap().to_string()
};
*format_string = format_string.replace("%{bcrypt}", &crypt);
}
if format_string.contains("%{des}") {
let crypt = if c.salt.is_some() {
test_crypt_result(
&unix_crypt::hash_with(c.salt.as_ref().unwrap().as_str(), pw),
c,
)
} else {
unix_crypt::hash(pw).unwrap().to_string()
};
*format_string = format_string.replace("%{des}", &crypt);
}
if format_string.contains("%{sha1}") {
let crypt = if c.salt.is_some() {
test_crypt_result(
&sha1_crypt::hash_with(c.salt.as_ref().unwrap().as_str(), pw),
c,
)
} else {
sha1_crypt::hash(pw).unwrap().to_string()
};
*format_string = format_string.replace("%{sha1}", &crypt);
}
if format_string.contains("%{sha256}") {
let crypt = if c.salt.is_some() {
test_crypt_result(
&sha256_crypt::hash_with(c.salt.as_ref().unwrap().as_str(), pw),
c,
)
} else {
sha256_crypt::hash(pw).unwrap().to_string()
};
*format_string = format_string.replace("%{sha256}", &crypt);
}
if format_string.contains("%{sha512}") {
let crypt = if c.salt.is_some() {
test_crypt_result(
&sha512_crypt::hash_with(c.salt.as_ref().unwrap().as_str(), pw),
c,
)
} else {
sha512_crypt::hash(pw).unwrap().to_string()
};
*format_string = format_string.replace("%{sha512}", &crypt);
}
if format_string.contains("%{password}") {
*format_string = format_string.replace("%{password}", pw);
}
if format_string.contains("%{username}") && c.username.is_some() {
*format_string = format_string.replace("%{username}", c.username.as_ref().unwrap());
}
if format_string.contains("%{database}") && c.database.is_some() {
*format_string = format_string.replace("%{database}", c.database.as_ref().unwrap());
}
if format_string.contains("%{mysql}") {
*format_string = format_string.replace("%{mysql}", &mysql_pass(pw).to_string());
}
if format_string.contains("%{mysql_caching_sha2}") {
*format_string = format_string.replace(
"%{mysql_caching_sha2}",
&mysql_caching_sha2_pass(pw).to_string(),
);
}
if format_string.contains("%{postgres}") {
*format_string = format_string.replace("%{postgres}", &postgres_pass(pw));
}
if format_string.contains("%{username}") && c.username.is_some() {
*format_string = format_string.replace("%{username}", c.username.as_ref().unwrap());
}
if format_string.contains("%{database}") && c.database.is_some() {
*format_string = format_string.replace("%{database}", c.database.as_ref().unwrap());
}
if format_string.contains("%{mysql}") {
*format_string = format_string.replace("%{mysql}", &mysql_pass(pw));
}
if format_string.contains("%{servername}") && c.servername.is_some() {
*format_string = format_string.replace("%{servername}", c.servername.as_ref().unwrap());
}
if format_string.contains("%{totp}") && c.totp_key.is_some() {
*format_string = format_string.replace("%{totp}", &totp_string(c));
}
if format_string.contains("%{totpsecs}") && c.totp_key.is_some() {
*format_string = format_string.replace("%{totpsecs}", &totp_secs_remaining(c).to_string());
}
if format_string.contains("%{totpsecsmax}") && c.totp_key.is_some() {
*format_string = format_string.replace("%{totpsecsmax}", &c.totp_step.unwrap().to_string());
}
if format_string.contains("%{totpprogress}") && c.totp_key.is_some() {
*format_string = format_string.replace("%{totpprogress}", &totp_progress(c));
}
if format_string.contains("%{totpalgo}") && c.totp_key.is_some() {
*format_string = format_string.replace(
"%{totpalgo}",
match c.totp_algo {
TotpAlgo::Sha1 => "sha1",
TotpAlgo::Sha256 => "sha256",
TotpAlgo::Sha512 => "sha512",
},
);
}
*format_string = format_string.replace(r#"\n"#, "\n");
format_string.to_string()
}
pub fn create_pg_database(c: &Config) -> String {
if c.create_database {
let db = c.database.as_ref().unwrap_or(&"".to_string()).to_string();
let username = c.username.as_ref().unwrap_or(&"".to_string()).to_string();
return format!(" create database {db}; alter database {db} owner to {username}; ")
.to_string();
}
"".to_string()
}
pub fn create_mysql_database(c: &Config) -> String {
if c.create_database {
return format!(
"create database {}; ",
c.database.as_ref().unwrap_or(&"".to_string())
)
.to_string();
}
"".to_string()
}
pub fn create_nginx_vhost() -> String {
let vhost = r#"
server {
listen 80;
listen [::]:80;
server_name %{servername} www.%{servername};
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
listen [::]:443 ssl;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH';
server_name %{servername} www.%{servername};
access_log /var/log/nginx/%{servername}_access.log;
error_log /var/log/nginx/%{servername}_error.log;
include /etc/nginx/snippets/snakeoil.conf;
location / {
}
}
"#;
vhost.to_string()
}
pub fn create_apache_vhost() -> String {
let vhost = r#"
<VirtualHost *:80>
ServerName %{servername}
ServerAlias www.%{servername}
DocumentRoot /home/%{username}/public_html
ErrorLog ${APACHE_LOG_DIR}/%{servername}_error.log
CustomLog ${APACHE_LOG_DIR}/%{servername}_access.log combined
<Directory "/home/%{username}/public_html">
Require all granted
AllowOverride All
</Directory>
</VirtualHost>
"#;
vhost.to_string()
}
pub fn totp_string(c: &Config) -> String {
fn to_bytes(n: u64) -> [u8; 8] {
let mask: u64 = 0xff;
let mut bytes: [u8; 8] = [0; 8];
(0..8).for_each(|i| bytes[7 - i] = (mask & (n >> (i * 8))) as u8);
bytes
}
fn totp_maker(c: &Config, pw: &[u8]) -> String {
let mut hash: Vec<u8> = vec![];
match c.totp_algo {
TotpAlgo::Sha1 => {
let mut mac = Hmac::<Sha1>::new_from_slice(pw).unwrap();
Hmac::<Sha1>::update(&mut mac, &to_bytes(c.totp_seconds() / c.totp_step.unwrap()));
hash.extend_from_slice(&mac.finalize().into_bytes());
}
TotpAlgo::Sha256 => {
let mut mac = Hmac::<Sha256>::new_from_slice(pw).unwrap();
Hmac::<Sha256>::update(
&mut mac,
&to_bytes(c.totp_seconds() / c.totp_step.unwrap()),
);
hash.extend_from_slice(&mac.finalize().into_bytes());
}
TotpAlgo::Sha512 => {
let mut mac = Hmac::<Sha512>::new_from_slice(pw).unwrap();
Hmac::<Sha512>::update(
&mut mac,
&to_bytes(c.totp_seconds() / c.totp_step.unwrap()),
);
hash.extend_from_slice(&mac.finalize().into_bytes());
}
}
let offset: usize = (hash.last().unwrap() & 0xf) as usize;
let binary: u64 = (((hash[offset] & 0x7f) as u64) << 24)
| ((hash[offset + 1] as u64) << 16)
| ((hash[offset + 2] as u64) << 8)
| (hash[offset + 3] as u64);
format!(
"{:01$}",
binary % (10_u64.pow(c.len.unwrap_or(6))),
c.len.unwrap_or(6) as usize
)
}
let s = c
.totp_key
.as_ref()
.unwrap()
.trim()
.to_lowercase()
.to_string();
let password: &[u8] = &match base32::decode(Alphabet::Rfc4648Lower { padding: false }, &s) {
Some(x) => x,
None => {
eprintln!("Cannot base32 convert {}", &s);
std::process::exit(1);
}
};
totp_maker(c, password)
}
pub fn totp_secs_remaining(c: &Config) -> u64 {
c.totp_step.unwrap() - c.totp_seconds() % c.totp_step.unwrap()
}
pub fn totp_progress_bar(c: &Config) -> String {
let width: usize = 30;
let percent: usize = width / (c.totp_step.unwrap() as usize);
let remaining: usize = (c.totp_step.unwrap() as usize)
- (c.totp_seconds() as usize) % (c.totp_step.unwrap() as usize);
let progress = percent * remaining;
let padding = width - progress;
format!(
"{empty:#<progress$}{empty: ^padding$}",
empty = "",
progress = progress,
padding = padding,
)
}
pub fn totp_progress(c: &Config) -> String {
let mut ret = "".to_string();
ret.push_str(&urgent_colour(totp_secs_remaining(c) as i32));
ret.push_str(&totp_progress_bar(c));
ret.push_str(&normal_colour());
ret.to_string()
}
pub fn set_wordlist(c: &mut Config) {
if c.word_list.is_some() {
let word_list = c.word_list.as_ref().unwrap();
if c.words.is_none() {
let f = File::open(word_list);
if f.is_err() {
eprintln!("Cannot open {}: {}", word_list, f.err().unwrap());
std::process::exit(1);
}
let f = f.unwrap();
let mut line = String::new();
let mut br = BufReader::new(f);
let mut wv: Vec<String> = vec![];
loop {
line.clear();
let l = br.read_line(&mut line);
match l {
Ok(i) => {
if i == 0 {
break;
}
}
Err(_) => {
break;
}
}
if line.find('\'').is_some() {
continue;
}
let s = line.clone().trim().to_string();
if s.is_empty() {
continue;
}
if !valid_word(&s, c.pw_type) {
continue;
}
wv.push(s);
}
c.words = Some(wv);
}
}
}
pub fn new_db(c: &Config) {
let key = CompositeKey::from_password(c.keepassphrase.as_ref().unwrap());
let path = c.keepassdb.as_ref().unwrap();
if !std::path::Path::exists(std::path::Path::new(path)) {
let mut db_file = File::create(path).unwrap();
let mut db = Database::new(&key);
db.db_type = kpdb::DbType::Kdb2;
db.history_max_items = 100;
if let Err(x) = db.save(&mut db_file) {
eprintln!("Cannot create {}: {}", path, x);
std::process::exit(1);
}
}
}
pub fn keepass_item_name(c: &Config) -> String {
let mut item: Vec<String> = vec![];
if let Some(i) = &c.servername {
item.push(i.to_string());
}
if let Some(i) = &c.database {
item.push(i.to_string());
}
item.join("-")
}
pub fn get_keepass_pw(c: &mut Config) {
if let Some(keepass_path) = &c.keepassdb {
if c.keepassphrase.is_none()
&& !std::path::Path::exists(std::path::Path::new(&keepass_path))
{
eprintln!("Cannot open {}", keepass_path);
std::process::exit(1);
}
}
let key = CompositeKey::from_password(c.keepassphrase.as_ref().unwrap());
let path = c.keepassdb.as_ref().unwrap();
let mut db_file = File::open(path).unwrap_or_else(|_| panic!("Cannot open {}", path));
let mut db =
Database::open(&mut db_file, &key).unwrap_or_else(|_| panic!("Cannot read {}", path));
let item = keepass_item_name(c);
for e in db.root_group.entries.iter_mut() {
if let Some(title) = e.title() {
if title == item {
if let Some(pw) = e.password() {
c.password = Some(pw.to_string());
return;
}
}
}
}
eprintln!("Cannot find password in {}", path);
std::process::exit(1);
}
pub fn update_keepass(c: &Config, pw: &str, item: &str) {
let key = CompositeKey::from_password(c.keepassphrase.as_ref().unwrap());
let path = c.keepassdb.as_ref().unwrap();
let mut db_file = File::open(path).unwrap_or_else(|_| panic!("Cannot open {}", path));
let mut db =
Database::open(&mut db_file, &key).unwrap_or_else(|_| panic!("Cannot read {}", path));
let mut found = false;
for e in db.root_group.entries.iter_mut() {
if c.username.is_some()
&& e.username().is_some()
&& c.username.as_ref().unwrap() != e.username().as_ref().unwrap()
{
continue;
}
if let Some(title) = e.title() {
if title == item {
e.history.push(e.clone());
e.set_title(item);
if let Some(user) = &c.username {
e.set_username(user);
}
e.set_password(pw);
e.last_modified = Local::now().into();
found = true;
}
}
}
if !found {
let mut entry = Entry::new();
entry.set_title(item);
if let Some(user) = &c.username {
entry.set_username(user);
}
entry.set_password(pw);
db.root_group.add_entry(entry);
}
let mut db_file = File::create(path).unwrap();
let _ = db.save(&mut db_file);
}
pub fn update_keepassdb(c: &mut Config, pw: &str) {
if c.keepassfetch {
return;
}
if let Some(keepass_path) = &c.keepassdb {
if c.keepassphrase.is_none()
&& !std::path::Path::exists(std::path::Path::new(&keepass_path))
{
let passphrase = prng_string(c);
c.keepassphrase = Some(passphrase);
println!(
"Using {} as the passphrase for {} as it does not exist",
&c.keepassphrase.as_ref().unwrap(),
keepass_path
);
}
if c.keepassphrase.is_some() && c.keepassdb.is_some() {
new_db(c);
update_keepass(c, pw, &keepass_item_name(c));
}
}
}
#[allow(deprecated)]
pub fn pw_loop(matches: &getopts::Matches, c: &mut Config) {
if c.keepassfetch {
get_keepass_pw(c);
}
let pw = (if c.password.is_some() {
c.password.as_ref().unwrap().to_string()
} else {
(*prng_string(c)).to_string()
})
.to_string();
let mut donefmtopt = false;
if matches.opt_present("userfmt") {
let mut format_string = "%{userfmt} # %{password}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("usermodfmt") {
let mut format_string = "%{usermodfmt} # %{password}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("pgfmt") {
let mut format_string =
format!("%{{pgfmt}}{} -- # %{{password}}\n", create_pg_database(c)).to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("mysqlfmt") {
let mut format_string = format!(
"{}%{{mysqlfmt}} -- # %{{password}}\n",
create_mysql_database(c)
)
.to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("mysqluserfmt") {
let mut format_string = format!(
"{}%{{mysqluserfmt}} -- # %{{password}}\n",
create_mysql_database(c)
)
.to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("wpconfigfmt") {
let mut format_string = "%{wpconfigfmt} # %{password}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("wpinstfmt") {
let mut format_string = "%{wpinstfmt} # %{password}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("wpuserfmt") {
let mut format_string = "%{wpuserfmt} -- %{password}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("htauthfmt") {
let mut format_string = "# %{password}\n%{htauthfmt}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("nginxfmt") {
let mut format_string = create_nginx_vhost();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("apachefmt") {
let mut format_string = create_apache_vhost();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("totpfmt") {
let mut format_string = "%{totpfmt}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("format") {
let mut format_string = matches.opt_str("format").unwrap().to_string();
if format_string.trim() == "useradd" {
format_string = "%{userfmt} # %{password}\n".to_string();
}
if format_string.trim() == "usermod" {
format_string = "%{usermodfmt} # %{password}\n".to_string();
}
if format_string.trim() == "mysql" {
format_string = "%{mysqlfmt} -- %{password}\n".to_string();
}
if format_string.trim() == "pg" {
format_string = "%{pgfmt} -- %{password}\n".to_string();
}
if format_string.trim() == "wpuserfmt" {
format_string = "%{wpuserfmt} -- %{password}\n".to_string();
}
if format_string.trim() == "wpinstfmt" {
format_string = "%{wpinstfmt} # %{password}\n".to_string();
}
if format_string.trim() == "htauth" {
format_string = "# %{password}\n%{htauthfmt}\n".to_string();
}
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
update_keepassdb(c, &pw);
return;
}
if donefmtopt {
update_keepassdb(c, &pw);
return;
}
if matches.opt_present("des") {
let mut format_string = "%{des} # %{password}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("md5") {
let mut format_string = "%{md5} # %{password}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("sha1") {
let mut format_string = "%{sha1} # %{password}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("sha256") {
let mut format_string = "%{sha256} # %{password}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("sha512") {
let mut format_string = "%{sha512} # %{password}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if matches.opt_present("bcrypt") {
let mut format_string = "%{bcrypt} # %{password}\n".to_string();
format_string = process_format_string(&mut format_string, c, &pw);
print!("{}", format_string);
donefmtopt = true;
}
if donefmtopt {
update_keepassdb(c, &pw);
return;
}
let mut outputs = vec![];
let max_counter = if c.number.is_some() { 2 } else { 8 };
for _ in 1..max_counter {
let pw = (if c.password.is_some() {
c.password.as_ref().unwrap().to_string()
} else {
(*prng_string(c)).to_string()
})
.to_string();
outputs.push(pw.clone());
update_keepassdb(c, &pw);
if c.word_list.is_some() {
break;
}
}
println!("{}", outputs.join(" "));
}
fn parse_totp_item(c: &mut Config, items: Vec<&str>) {
match items[0].to_lowercase().as_str() {
"key" | "totp" => {
c.totp_key = Some(items[1].to_string());
}
"name" | "username" => {
c.username = Some(items[1].to_string());
}
"step" => {
c.totp_step = match items[1].parse::<u32>() {
Ok(l) => Some(l.into()),
Err(_) => {
eprintln!("cannot convert {} to number", items[1]);
std::process::exit(1);
}
}
}
"algo" => {
c.totp_algo = match items[1].to_lowercase().as_str() {
"sha1" => TotpAlgo::Sha1,
"sha256" => TotpAlgo::Sha256,
"sha512" => TotpAlgo::Sha512,
_ => {
eprintln!("cannot convert {} to a TOTP algorithm", items[1]);
std::process::exit(1);
}
}
}
"digits" => {
c.len = match items[1].parse::<u32>() {
Ok(l) => Some(l),
Err(_) => {
eprintln!("cannot convert {} to number", items[1]);
std::process::exit(1);
}
}
}
"seconds" => {
c.totp_seconds = match items[1].parse::<u32>() {
Ok(l) => Some(l.into()),
Err(_) => {
eprintln!("cannot convert {} to number", items[1]);
std::process::exit(1);
}
}
}
_ => {
eprintln!("key {} isn't valid", items[0]);
std::process::exit(1);
}
}
}
pub fn totp_mode(matches: &getopts::Matches) -> bool {
if matches.opt_present("totpfmt") {
return true;
}
if matches.opt_present("totp") {
return true;
}
false
}
pub fn main_loop(matches: &getopts::Matches, c: &mut Config) {
for _ in 0..c.number.unwrap_or(20) {
if c.totp_key.is_some() && totp_mode(matches) {
let totp_arr: Vec<_> = c.totp_key.as_ref().unwrap().split(";").collect();
for i in totp_arr {
let i = i.trim();
if i.is_empty() {
continue;
}
let c: &mut Config = &mut c.clone();
c.totp_key = Some(i.to_string());
let parts: Vec<_> = i.split(",").collect();
for p in parts {
let p = p.trim();
if p.is_empty() {
continue;
}
let items: Vec<_> = p.splitn(2, "=").collect();
if items.len() < 2 {
continue;
}
parse_totp_item(c, items);
}
pw_loop(matches, c);
}
continue;
}
pw_loop(matches, c);
}
}
pub fn sleep(millis: u64) {
let duration = Duration::from_millis(millis);
thread::sleep(duration);
}
pub fn urgent_colour(remaining: i32) -> String {
if remaining < 6 {
"\x1b[0;31m".to_string()
} else {
"\x1b[0;32m".to_string()
}
}
pub fn normal_colour() -> String {
"\x1b[0m".to_string()
}