From 563e0cfa3250ed6e9a50ac63a2e72ed359e50be7 Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Sun, 21 Apr 2024 21:57:17 +0300 Subject: [PATCH 01/10] remove useless cfg_if --- rookie-rs/src/browser/chromium.rs | 76 ++++----- rookie-rs/src/lib.rs | 261 +++++++++++++++--------------- 2 files changed, 169 insertions(+), 168 deletions(-) diff --git a/rookie-rs/src/browser/chromium.rs b/rookie-rs/src/browser/chromium.rs index a33a5b7..536b0e7 100644 --- a/rookie-rs/src/browser/chromium.rs +++ b/rookie-rs/src/browser/chromium.rs @@ -53,39 +53,40 @@ fn get_keys(config: &BrowserConfig) -> Result>> { let mut keys: Vec> = vec![]; - cfg_if! { - if #[cfg(target_os = "linux")] { - if let Ok(passwords) = secrets::get_passwords(config.os_crypt_name.unwrap_or("")) { - for password in passwords { - let key = create_pbkdf2_key(password.as_str(), salt, iterations); - keys.push(key); - } - } - // default keys - let key = create_pbkdf2_key("peanuts", salt, iterations); - keys.push(key); - let key = create_pbkdf2_key("", salt, iterations); - keys.push(key); - } else if #[cfg(target_os = "macos")] { - let key_service = config.osx_key_service.context("missing osx_key_service")?; - let key_user = config.osx_key_user.context("missing osx_key_user")?; - let password = secrets - ::get_osx_keychain_password(key_service, key_user) - .unwrap_or("peanuts".to_string()); - - let key = create_pbkdf2_key(password.as_str(), salt, iterations); - keys.push(key); - - let key = create_pbkdf2_key("peanuts", salt, iterations); - keys.push(key); - let key = create_pbkdf2_key("", salt, iterations); - keys.push(key); + #[cfg(target_os = "linux")] + { + if let Ok(passwords) = secrets::get_passwords(config.os_crypt_name.unwrap_or("")) { + for password in passwords { + let key = create_pbkdf2_key(password.as_str(), salt, iterations); + keys.push(key); } + } + // default keys + let key = create_pbkdf2_key("peanuts", salt, iterations); + keys.push(key); + let key = create_pbkdf2_key("", salt, iterations); + keys.push(key); + } + #[cfg(target_os = "macos")] + { + let key_service = config.osx_key_service.context("missing osx_key_service")?; + let key_user = config.osx_key_user.context("missing osx_key_user")?; + let password = + secrets::get_osx_keychain_password(key_service, key_user).unwrap_or("peanuts".to_string()); + + let key = create_pbkdf2_key(password.as_str(), salt, iterations); + keys.push(key); + + let key = create_pbkdf2_key("peanuts", salt, iterations); + keys.push(key); + let key = create_pbkdf2_key("", salt, iterations); + keys.push(key); + } - // keychain key + // keychain key + + // default keys - // default keys - } Ok(keys) } @@ -175,14 +176,13 @@ fn query_cookies( db_path: PathBuf, domains: Option>, ) -> Result> { - cfg_if! { - if #[cfg(target_os = "windows")] { - let db_path_str = db_path.to_str().context("Can't convert db path to str")?; - warn!("Unlocking Chrome database... This may take a while (sometimes up to a minute)"); - unsafe { - winapi::release_file_lock(db_path_str); - } - } + #[cfg(target_os = "windows")] + { + let db_path_str = db_path.to_str().context("Can't convert db path to str")?; + warn!("Unlocking Chrome database... This may take a while (sometimes up to a minute)"); + unsafe { + winapi::release_file_lock(db_path_str); + } } info!( diff --git a/rookie-rs/src/lib.rs b/rookie-rs/src/lib.rs index ec8f7c3..6812897 100644 --- a/rookie-rs/src/lib.rs +++ b/rookie-rs/src/lib.rs @@ -3,22 +3,21 @@ pub mod common; pub mod config; use browser::{chromium::chromium_based, mozilla::firefox_based}; -use cfg_if::cfg_if; use common::{enums::Cookie, paths}; use eyre::{bail, Result}; + +#[cfg(target_os = "macos")] +use browser::safari::safari_based; + +#[cfg(target_os = "windows")] +use browser::internet_explorer; +#[cfg(target_os = "windows")] +use common::winapi; +#[cfg(target_os = "windows")] +pub use internet_explorer::internet_explorer_based; #[cfg(target_os = "windows")] use std::path::PathBuf; -cfg_if! { - if #[cfg(target_os = "windows")] { - use common::winapi; - use browser::internet_explorer; - pub use internet_explorer::internet_explorer_based; - } else if #[cfg(target_os = "macos")] { - use browser::safari::safari_based; - } -} - /// Returns cookies from Firefox /// /// # Arguments @@ -84,14 +83,15 @@ pub fn cachy(domains: Option>) -> Result> { /// let cookies = rookie::chrome(Some(domains)); /// ``` pub fn chrome(domains: Option>) -> Result> { - cfg_if! { - if #[cfg(target_os = "windows")] { - let (key, db_path) = paths::find_chrome_based_paths(&config::CHROME_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) - } else { - let (_, db_path) = paths::find_chrome_based_paths(&config::CHROME_CONFIG)?; - chromium_based(&config::CHROME_CONFIG, db_path, domains) - } + #[cfg(target_os = "windows")] + { + let (key, db_path) = paths::find_chrome_based_paths(&config::CHROME_CONFIG)?; + chromium_based(PathBuf::from(key), db_path, domains) + } + #[cfg(unix)] + { + let (_, db_path) = paths::find_chrome_based_paths(&config::CHROME_CONFIG)?; + chromium_based(&config::CHROME_CONFIG, db_path, domains) } } @@ -108,14 +108,15 @@ pub fn chrome(domains: Option>) -> Result> { /// let cookies = rookie::chromium(Some(domains)); /// ``` pub fn chromium(domains: Option>) -> Result> { - cfg_if! { - if #[cfg(target_os = "windows")] { - let (key, db_path) = paths::find_chrome_based_paths(&config::CHROMIUM_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) - } else { - let (_, db_path) = paths::find_chrome_based_paths(&config::CHROMIUM_CONFIG)?; - chromium_based(&config::CHROMIUM_CONFIG, db_path, domains) - } + #[cfg(target_os = "windows")] + { + let (key, db_path) = paths::find_chrome_based_paths(&config::CHROMIUM_CONFIG)?; + chromium_based(PathBuf::from(key), db_path, domains) + } + #[cfg(unix)] + { + let (_, db_path) = paths::find_chrome_based_paths(&config::CHROMIUM_CONFIG)?; + chromium_based(&config::CHROMIUM_CONFIG, db_path, domains) } } @@ -132,14 +133,15 @@ pub fn chromium(domains: Option>) -> Result> { /// let cookies = rookie::brave(Some(domains)); /// ``` pub fn brave(domains: Option>) -> Result> { - cfg_if! { - if #[cfg(target_os = "windows")] { - let (key, db_path) = paths::find_chrome_based_paths(&config::BRAVE_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) - } else { - let (_, db_path) = paths::find_chrome_based_paths(&config::BRAVE_CONFIG)?; - chromium_based(&config::BRAVE_CONFIG, db_path, domains) - } + #[cfg(target_os = "windows")] + { + let (key, db_path) = paths::find_chrome_based_paths(&config::BRAVE_CONFIG)?; + chromium_based(PathBuf::from(key), db_path, domains) + } + #[cfg(unix)] + { + let (_, db_path) = paths::find_chrome_based_paths(&config::BRAVE_CONFIG)?; + chromium_based(&config::BRAVE_CONFIG, db_path, domains) } } @@ -156,14 +158,15 @@ pub fn brave(domains: Option>) -> Result> { /// let cookies = rookie::edge(Some(domains)); /// ``` pub fn edge(domains: Option>) -> Result> { - cfg_if! { - if #[cfg(target_os = "windows")] { - let (key, db_path) = paths::find_chrome_based_paths(&config::EDGE_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) - } else { - let (_, db_path) = paths::find_chrome_based_paths(&config::EDGE_CONFIG)?; - chromium_based(&config::EDGE_CONFIG, db_path, domains) - } + #[cfg(target_os = "windows")] + { + let (key, db_path) = paths::find_chrome_based_paths(&config::EDGE_CONFIG)?; + chromium_based(PathBuf::from(key), db_path, domains) + } + #[cfg(unix)] + { + let (_, db_path) = paths::find_chrome_based_paths(&config::EDGE_CONFIG)?; + chromium_based(&config::EDGE_CONFIG, db_path, domains) } } @@ -180,14 +183,15 @@ pub fn edge(domains: Option>) -> Result> { /// let cookies = rookie::vivaldi(Some(domains)); /// ``` pub fn vivaldi(domains: Option>) -> Result> { - cfg_if! { - if #[cfg(target_os = "windows")] { - let (key, db_path) = paths::find_chrome_based_paths(&config::VIVALDI_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) - } else { - let (_, db_path) = paths::find_chrome_based_paths(&config::VIVALDI_CONFIG)?; - chromium_based(&config::VIVALDI_CONFIG, db_path, domains) - } + #[cfg(target_os = "windows")] + { + let (key, db_path) = paths::find_chrome_based_paths(&config::VIVALDI_CONFIG)?; + chromium_based(PathBuf::from(key), db_path, domains) + } + #[cfg(unix)] + { + let (_, db_path) = paths::find_chrome_based_paths(&config::VIVALDI_CONFIG)?; + chromium_based(&config::VIVALDI_CONFIG, db_path, domains) } } @@ -204,14 +208,15 @@ pub fn vivaldi(domains: Option>) -> Result> { /// let cookies = rookie::opera(Some(domains)); /// ``` pub fn opera(domains: Option>) -> Result> { - cfg_if! { - if #[cfg(target_os = "windows")] { - let (key, db_path) = paths::find_chrome_based_paths(&config::OPERA_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) - } else { - let (_, db_path) = paths::find_chrome_based_paths(&config::OPERA_CONFIG)?; - chromium_based(&config::OPERA_CONFIG, db_path, domains) - } + #[cfg(target_os = "windows")] + { + let (key, db_path) = paths::find_chrome_based_paths(&config::OPERA_CONFIG)?; + chromium_based(PathBuf::from(key), db_path, domains) + } + #[cfg(unix)] + { + let (_, db_path) = paths::find_chrome_based_paths(&config::OPERA_CONFIG)?; + chromium_based(&config::OPERA_CONFIG, db_path, domains) } } @@ -228,14 +233,15 @@ pub fn opera(domains: Option>) -> Result> { /// let cookies = rookie::opera_gx(Some(domains)); /// ``` pub fn opera_gx(domains: Option>) -> Result> { - cfg_if! { - if #[cfg(target_os = "windows")] { - let (key, db_path) = paths::find_chrome_based_paths(&config::OPERA_GX_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) - } else { - let (_, db_path) = paths::find_chrome_based_paths(&config::OPERA_GX_CONFIG)?; - chromium_based(&config::OPERA_GX_CONFIG, db_path, domains) - } + #[cfg(target_os = "windows")] + { + let (key, db_path) = paths::find_chrome_based_paths(&config::OPERA_GX_CONFIG)?; + chromium_based(PathBuf::from(key), db_path, domains) + } + #[cfg(unix)] + { + let (_, db_path) = paths::find_chrome_based_paths(&config::OPERA_GX_CONFIG)?; + chromium_based(&config::OPERA_GX_CONFIG, db_path, domains) } } @@ -309,19 +315,23 @@ pub fn load(domains: Option>) -> Result> { let mut cookies = Vec::new(); let mut browser_types = vec![firefox, librewolf, opera, edge, chromium, brave, vivaldi]; - cfg_if! { - if #[cfg(target_os = "windows")] { - browser_types.push(chrome); - browser_types.push(internet_explorer); - browser_types.push(opera_gx); - } else if #[cfg(target_os = "linux")] { - browser_types.push(chrome); - browser_types.push(cachy); - } else if #[cfg(target_os = "macos")] { - browser_types.push(chrome); - browser_types.push(opera_gx); - browser_types.push(safari); - } + + #[cfg(target_os = "windows")] + { + browser_types.push(chrome); + browser_types.push(internet_explorer); + browser_types.push(opera_gx); + } + #[cfg(target_os = "linux")] + { + browser_types.push(chrome); + browser_types.push(cachy); + } + #[cfg(target_os = "macos")] + { + browser_types.push(chrome); + browser_types.push(opera_gx); + browser_types.push(safari); } for browser_fn in browser_types.iter() { @@ -333,6 +343,7 @@ pub fn load(domains: Option>) -> Result> { } /// Returns cookies from specific browser +/// Useful for CLI apps /// /// # Arguments /// @@ -355,64 +366,54 @@ pub fn any_browser( key_path: Option<&str>, ) -> Result> { // chromium based - cfg_if! { - // Linux Chromium - if #[cfg(unix)] { - use crate::config; - let chrome_configs = &[ - &config::CHROME_CONFIG, - &config::BRAVE_CONFIG, - &config::CHROMIUM_CONFIG, - &config::EDGE_CONFIG, - &config::OPERA_CONFIG, - &config::OPERA_GX_CONFIG, - &config::VIVALDI_CONFIG, - ]; - for browser_config in chrome_configs { - if - let Ok(cookies) = chromium_based( - browser_config, - cookies_path.into(), - domains.clone() - ) - { - return Ok(cookies); - } - } - } else { - if let Some(key_path) = key_path { - if - let Ok(cookies) = chromium_based( - PathBuf::from(key_path), - cookies_path.into(), - domains.clone() - ) - { - return Ok(cookies); - } - } + #[cfg(unix)] + { + let chrome_configs = &[ + &config::CHROME_CONFIG, + &config::BRAVE_CONFIG, + &config::CHROMIUM_CONFIG, + &config::EDGE_CONFIG, + &config::OPERA_CONFIG, + &config::OPERA_GX_CONFIG, + &config::VIVALDI_CONFIG, + ]; + for browser_config in chrome_configs { + if let Ok(cookies) = chromium_based(browser_config, cookies_path.into(), domains.clone()) { + return Ok(cookies); } - // Windows chromium + } + } + #[cfg(target_os = "windows")] + { + if let Some(key_path) = key_path { + if let Ok(cookies) = chromium_based( + PathBuf::from(key_path), + cookies_path.into(), + domains.clone(), + ) { + return Ok(cookies); + } + } } + // Windows chromium // Firefox if let Ok(cookies) = firefox_based(cookies_path.into(), domains.clone()) { return Ok(cookies); } - cfg_if! { - if #[cfg(target_os = "windows")] { - // Internet Explorer - if let Ok(cookies) = internet_explorer_based(cookies_path.into(), domains.clone()) { - return Ok(cookies); - } - } else if #[cfg(target_os = "macos")] { - if let Ok(cookies) = safari_based(cookies_path.into(), domains) { - return Ok(cookies); - } - } - // Safari - // try safari based + #[cfg(target_os = "windows")] + { + // Internet Explorer + if let Ok(cookies) = internet_explorer_based(cookies_path.into(), domains.clone()) { + return Ok(cookies); + } + } + #[cfg(target_os = "macos")] + { + if let Ok(cookies) = safari_based(cookies_path.into(), domains) { + return Ok(cookies); + } } bail!("Can't find any cookies"); } From cf8ac5b17c965d28e598d0ded1168843b700f399 Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Sun, 21 Apr 2024 22:53:43 +0300 Subject: [PATCH 02/10] refactor from macos --- bindings/README.md | 7 - rookie-rs/examples/chrome/Cargo.toml | 10 - rookie-rs/examples/chrome/src/main.rs | 4 - rookie-rs/{bin/main.rs => examples/simple.rs} | 0 rookie-rs/src/browser/chromium.rs | 172 ++++---- rookie-rs/src/browser/mozilla.rs | 3 +- rookie-rs/src/browser/safari.rs | 68 +-- rookie-rs/src/common/date.rs | 20 +- rookie-rs/src/common/mod.rs | 6 - rookie-rs/src/common/paths.rs | 86 ++-- rookie-rs/src/common/secrets.rs | 152 ------- rookie-rs/src/common/utils.rs | 9 - rookie-rs/src/config.rs | 389 ------------------ rookie-rs/src/lib.rs | 27 +- rookie-rs/src/linux/config.rs | 122 ++++++ rookie-rs/src/linux/mod.rs | 5 + rookie-rs/src/linux/secrets.rs | 119 ++++++ rookie-rs/src/macos/config.rs | 103 +++++ rookie-rs/src/macos/mod.rs | 2 + rookie-rs/src/macos/secrets.rs | 29 ++ rookie-rs/src/windows/config.rs | 154 +++++++ .../{common/winapi.rs => windows/dpapi.rs} | 0 rookie-rs/src/windows/file_unlock.rs | 60 +++ rookie-rs/src/windows/mod.rs | 3 + 24 files changed, 791 insertions(+), 759 deletions(-) delete mode 100644 bindings/README.md delete mode 100644 rookie-rs/examples/chrome/Cargo.toml delete mode 100644 rookie-rs/examples/chrome/src/main.rs rename rookie-rs/{bin/main.rs => examples/simple.rs} (100%) delete mode 100644 rookie-rs/src/common/secrets.rs delete mode 100644 rookie-rs/src/config.rs create mode 100644 rookie-rs/src/linux/config.rs create mode 100644 rookie-rs/src/linux/mod.rs create mode 100644 rookie-rs/src/linux/secrets.rs create mode 100644 rookie-rs/src/macos/config.rs create mode 100644 rookie-rs/src/macos/mod.rs create mode 100644 rookie-rs/src/macos/secrets.rs create mode 100644 rookie-rs/src/windows/config.rs rename rookie-rs/src/{common/winapi.rs => windows/dpapi.rs} (100%) create mode 100644 rookie-rs/src/windows/file_unlock.rs create mode 100644 rookie-rs/src/windows/mod.rs diff --git a/bindings/README.md b/bindings/README.md deleted file mode 100644 index 46c13f1..0000000 --- a/bindings/README.md +++ /dev/null @@ -1,7 +0,0 @@ -# Rookie bindings - -## Python - -```console -pip3 install rookiepy -``` diff --git a/rookie-rs/examples/chrome/Cargo.toml b/rookie-rs/examples/chrome/Cargo.toml deleted file mode 100644 index 1a9c3de..0000000 --- a/rookie-rs/examples/chrome/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "chrome" -version = "0.1.0" -edition = "2021" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - - -[dependencies] -rookie = { path = "../../", version = "*" } diff --git a/rookie-rs/examples/chrome/src/main.rs b/rookie-rs/examples/chrome/src/main.rs deleted file mode 100644 index b4c1d7c..0000000 --- a/rookie-rs/examples/chrome/src/main.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - let cookies = rookie::chrome(None).unwrap(); - println!("{cookies:?}"); -} diff --git a/rookie-rs/bin/main.rs b/rookie-rs/examples/simple.rs similarity index 100% rename from rookie-rs/bin/main.rs rename to rookie-rs/examples/simple.rs diff --git a/rookie-rs/src/browser/chromium.rs b/rookie-rs/src/browser/chromium.rs index 536b0e7..041a363 100644 --- a/rookie-rs/src/browser/chromium.rs +++ b/rookie-rs/src/browser/chromium.rs @@ -1,9 +1,11 @@ use crate::common::{date, enums::*, sqlite}; use cfg_if::cfg_if; use eyre::{bail, ContextCompat, Result}; -use log::{info, warn}; use std::path::PathBuf; +#[cfg(target_os = "macos")] +use crate::macos::secrets; + cfg_if! { if #[cfg(target_os = "windows")] { use aes_gcm::{ Aes256Gcm, Key, aead::{ Aead, KeyInit, generic_array::GenericArray } }; @@ -12,18 +14,42 @@ cfg_if! { use crate::winapi; use eyre::Context; } else if #[cfg(unix)] { - use crate::common::secrets; + } } #[cfg(target_os = "windows")] -fn get_keys(key64: &str) -> Result>> { - let mut keydpapi: Vec = general_purpose::STANDARD.decode(&key64)?; - let keydpapi = &mut keydpapi[5..]; - let v10_key = winapi::decrypt(keydpapi)?; - let mut keys: Vec> = vec![]; - keys.push(v10_key); - Ok(keys) +pub fn chromium_based( + key: PathBuf, + db_path: PathBuf, + domains: Option>, +) -> Result> { + // Use DPAPI + let content = std::fs::read_to_string(&key)?; + let key_dict: serde_json::Value = + serde_json::from_str(content.as_str()).context("Can't read json file")?; + + let os_crypt = key_dict.get("os_crypt").context("Can't get os crypt")?; + + let key64 = os_crypt + .get("encrypted_key") + .context("Can't get encrypted_key")? + .as_str() + .context("Can't convert encrypted_key to str")?; + + let keys = get_keys(key64)?; + query_cookies(keys, db_path, domains) +} + +#[cfg(unix)] +pub fn chromium_based( + config: &BrowserConfig, + db_path: PathBuf, + domains: Option>, +) -> Result> { + // Simple AES + let keys = get_keys(config)?; + query_cookies(keys, db_path, domains) } #[cfg(unix)] @@ -35,69 +61,73 @@ fn create_pbkdf2_key(password: &str, salt: &[u8; 9], iterations: u32) -> Vec output.to_vec() } -#[cfg(unix)] +#[cfg(target_os = "windows")] +fn get_keys(key64: &str) -> Result>> { + let mut keydpapi: Vec = general_purpose::STANDARD.decode(&key64)?; + let keydpapi = &mut keydpapi[5..]; + let v10_key = winapi::decrypt(keydpapi)?; + let mut keys: Vec> = vec![]; + keys.push(v10_key); + Ok(keys) +} + +#[cfg(target_os = "linux")] fn get_keys(config: &BrowserConfig) -> Result>> { // AES CBC key let salt = b"saltysalt"; let iterations: u32; - #[cfg(target_os = "linux")] - { - iterations = 1; - } - #[cfg(target_os = "macos")] - { - iterations = 1003; - } + iterations = 1; let mut keys: Vec> = vec![]; - - #[cfg(target_os = "linux")] - { - if let Ok(passwords) = secrets::get_passwords(config.os_crypt_name.unwrap_or("")) { - for password in passwords { - let key = create_pbkdf2_key(password.as_str(), salt, iterations); - keys.push(key); - } + if let Ok(passwords) = secrets::get_passwords(config.os_crypt_name.unwrap_or("")) { + for password in passwords { + let key = create_pbkdf2_key(password.as_str(), salt, iterations); + keys.push(key); } - // default keys - let key = create_pbkdf2_key("peanuts", salt, iterations); - keys.push(key); - let key = create_pbkdf2_key("", salt, iterations); - keys.push(key); - } - #[cfg(target_os = "macos")] - { - let key_service = config.osx_key_service.context("missing osx_key_service")?; - let key_user = config.osx_key_user.context("missing osx_key_user")?; - let password = - secrets::get_osx_keychain_password(key_service, key_user).unwrap_or("peanuts".to_string()); - - let key = create_pbkdf2_key(password.as_str(), salt, iterations); - keys.push(key); - - let key = create_pbkdf2_key("peanuts", salt, iterations); - keys.push(key); - let key = create_pbkdf2_key("", salt, iterations); - keys.push(key); } + // default keys + let key = create_pbkdf2_key("peanuts", salt, iterations); + keys.push(key); + let key = create_pbkdf2_key("", salt, iterations); + keys.push(key); - // keychain key + Ok(keys) +} - // default keys +#[cfg(target_os = "macos")] +fn get_keys(config: &BrowserConfig) -> Result>> { + let salt = b"saltysalt"; + let iterations: u32; + + iterations = 1003; + + let mut keys: Vec> = vec![]; + + let key_service = config.osx_key_service.context("missing osx_key_service")?; + let key_user = config.osx_key_user.context("missing osx_key_user")?; + let password = + secrets::get_osx_keychain_password(key_service, key_user).unwrap_or("peanuts".to_string()); + + let key = create_pbkdf2_key(password.as_str(), salt, iterations); + keys.push(key); + + let key = create_pbkdf2_key("peanuts", salt, iterations); + keys.push(key); + let key = create_pbkdf2_key("", salt, iterations); + keys.push(key); Ok(keys) } +/// Decrypt cookie value using aes GCM #[cfg(target_os = "windows")] fn decrypt_encrypted_value( value: String, encrypted_value: &[u8], keys: Vec>, ) -> Result { - // gcm - let key_type = &encrypted_value[..3]; if !value.is_empty() || !(key_type == b"v11" || key_type == b"v10") { // unknown key_type or value isn't encrypted @@ -122,6 +152,7 @@ fn decrypt_encrypted_value( bail!("decrypt_encrypted_value failed") } +/// Decrypt cookie value using aes cbc #[cfg(unix)] fn decrypt_encrypted_value( value: String, @@ -162,7 +193,7 @@ fn decrypt_encrypted_value( return Ok(decoded); } Err(_) => { - warn!("Error in decode decrypt value with utf8"); + log::warn!("Error in decode decrypt value with utf8"); return Ok("".into()); } } @@ -176,16 +207,17 @@ fn query_cookies( db_path: PathBuf, domains: Option>, ) -> Result> { + /// On windows release file lock #[cfg(target_os = "windows")] { let db_path_str = db_path.to_str().context("Can't convert db path to str")?; - warn!("Unlocking Chrome database... This may take a while (sometimes up to a minute)"); + log::warn!("Unlocking Chrome database... This may take a while (sometimes up to a minute)"); unsafe { winapi::release_file_lock(db_path_str); } } - info!( + log::info!( "Creating SQLite connection to {}", db_path.to_str().unwrap_or("") ); @@ -241,37 +273,3 @@ fn query_cookies( } Ok(cookies) } - -#[cfg(target_os = "windows")] -pub fn chromium_based( - key: PathBuf, - db_path: PathBuf, - domains: Option>, -) -> Result> { - // Use DPAPI - let content = std::fs::read_to_string(&key)?; - let key_dict: serde_json::Value = - serde_json::from_str(content.as_str()).context("Can't read json file")?; - - let os_crypt = key_dict.get("os_crypt").context("Can't get os crypt")?; - - let key64 = os_crypt - .get("encrypted_key") - .context("Can't get encrypted_key")? - .as_str() - .context("Can't convert encrypted_key to str")?; - - let keys = get_keys(key64)?; - query_cookies(keys, db_path, domains) -} - -#[cfg(unix)] -pub fn chromium_based( - config: &BrowserConfig, - db_path: PathBuf, - domains: Option>, -) -> Result> { - // Simple AES - let keys = get_keys(config)?; - query_cookies(keys, db_path, domains) -} diff --git a/rookie-rs/src/browser/mozilla.rs b/rookie-rs/src/browser/mozilla.rs index d6c20fe..afeecb4 100644 --- a/rookie-rs/src/browser/mozilla.rs +++ b/rookie-rs/src/browser/mozilla.rs @@ -1,7 +1,6 @@ use crate::common::{date, enums::*, sqlite, utils}; use eyre::{anyhow, bail, Result}; use ini::Ini; -use log::warn; use lz4_flex::block::decompress_size_prepended; use serde_json::Value; use std::{ @@ -38,7 +37,7 @@ pub fn firefox_based(db_path: PathBuf, domains: Option>) -> Result = row.get(0); if host.is_err() { // ignore null rows - warn!("host is NULL in row"); + log::warn!("host is NULL in row"); continue; } let host = host?; diff --git a/rookie-rs/src/browser/safari.rs b/rookie-rs/src/browser/safari.rs index 0514e97..2422845 100644 --- a/rookie-rs/src/browser/safari.rs +++ b/rookie-rs/src/browser/safari.rs @@ -3,6 +3,39 @@ use byteorder::{BigEndian, ByteOrder, LittleEndian}; use eyre::{anyhow, bail, Context, Result}; use std::{fs::File, io::Read, path::PathBuf, vec::Vec}; +/// 1. open cookies file +/// 2. parse headers +/// 3. parse pages (total from headers) +/// 4. get N cookies from each page, iterate +/// 5. parse each cookie +/// 6. add each cookie based on domain filter +pub fn safari_based(db_path: PathBuf, domains: Option>) -> Result> { + let mut file = + File::open(db_path.clone()).context(format!("failed to open {}", db_path.display()))?; + let mut bs: Vec = Vec::new(); + file.read_to_end(&mut bs)?; + let cookies = parse_content(&bs)?; + + // Filter cookies by domain if domains are specified + if let Some(domain_filters) = domains { + let filtered_cookies: Vec = cookies + .into_iter() + .filter(|cookie| { + // Check if the cookie's domain matches any of the specified domains + domain_filters.iter().any(|&domain| { + // Implement your domain matching logic here + // For example, you can use the `.ends_with` method to check if the cookie's domain ends with the specified domain. + cookie.domain.ends_with(domain) + }) + }) + .collect(); + + Ok(filtered_cookies) + } else { + Ok(cookies) + } +} + fn parse_page(bs: &[u8]) -> Result> { if slice(bs, 0, 4)? != [0x00, 0x00, 0x01, 0x00] { bail!("bad page header"); @@ -69,7 +102,7 @@ fn parse_cookie(bs: &[u8]) -> Result { Ok(cookie) } -pub fn parse_content(bs: &[u8]) -> Result> { +fn parse_content(bs: &[u8]) -> Result> { // Magic bytes: "COOK" = 0x636F6F6B if slice(bs, 0, 4)? != [0x63, 0x6f, 0x6f, 0x6b] { bail!("not a cookie file"); @@ -144,36 +177,3 @@ fn c_str(bs: &[u8]) -> Result { String::from_utf8(elements.to_vec()).map_err(|err| anyhow!(err.to_string())) }) } - -pub fn safari_based(db_path: PathBuf, domains: Option>) -> Result> { - // 1. open cookies file - // 2. parse headers - // 3. parse pages (total from headers) - // 4. get N cookies from each page, iterate - // 5. parse each cookie - // 6. add each cookie based on domain filter - let mut file = - File::open(db_path.clone()).context(format!("failed to open {}", db_path.display()))?; - let mut bs: Vec = Vec::new(); - file.read_to_end(&mut bs)?; - let cookies = parse_content(&bs)?; - - // Filter cookies by domain if domains are specified - if let Some(domain_filters) = domains { - let filtered_cookies: Vec = cookies - .into_iter() - .filter(|cookie| { - // Check if the cookie's domain matches any of the specified domains - domain_filters.iter().any(|&domain| { - // Implement your domain matching logic here - // For example, you can use the `.ends_with` method to check if the cookie's domain ends with the specified domain. - cookie.domain.ends_with(domain) - }) - }) - .collect(); - - Ok(filtered_cookies) - } else { - Ok(cookies) - } -} diff --git a/rookie-rs/src/common/date.rs b/rookie-rs/src/common/date.rs index 4a79066..70e44dd 100644 --- a/rookie-rs/src/common/date.rs +++ b/rookie-rs/src/common/date.rs @@ -11,16 +11,14 @@ pub fn mozilla_timestamp(timestamp: u64) -> Option { unix_timestamp(timestamp) } -#[cfg(target_os = "windows")] -pub fn internet_explorer_timestamp(timestamp: u64) -> Option { - if timestamp <= 0 { +fn unix_timestamp(timestamp: u64) -> Option { + if timestamp == 0 { return None; } - let mut timestamp = timestamp - 116_444_736_000_000_000; - timestamp /= 10_000_000; - unix_timestamp(timestamp) + Some(timestamp) } +#[cfg(target_os = "macos")] pub fn safari_timestamp(timestamp: u64) -> Option { if timestamp == 0 { return None; @@ -30,10 +28,12 @@ pub fn safari_timestamp(timestamp: u64) -> Option { let unix_timestamp = unix_timestamp / 1_000_000_000; Some(unix_timestamp) } - -fn unix_timestamp(timestamp: u64) -> Option { - if timestamp == 0 { +#[cfg(target_os = "windows")] +pub fn internet_explorer_timestamp(timestamp: u64) -> Option { + if timestamp <= 0 { return None; } - Some(timestamp) + let mut timestamp = timestamp - 116_444_736_000_000_000; + timestamp /= 10_000_000; + unix_timestamp(timestamp) } diff --git a/rookie-rs/src/common/mod.rs b/rookie-rs/src/common/mod.rs index c4d059b..0f256c9 100644 --- a/rookie-rs/src/common/mod.rs +++ b/rookie-rs/src/common/mod.rs @@ -3,9 +3,3 @@ pub mod enums; pub mod paths; pub mod sqlite; pub mod utils; - -#[cfg(unix)] -pub mod secrets; - -#[cfg(target_os = "windows")] -pub mod winapi; diff --git a/rookie-rs/src/common/paths.rs b/rookie-rs/src/common/paths.rs index ce38e78..dfba8cd 100644 --- a/rookie-rs/src/common/paths.rs +++ b/rookie-rs/src/common/paths.rs @@ -1,6 +1,5 @@ use crate::{browser::mozilla::get_default_profile, common::enums::BrowserConfig}; use eyre::{anyhow, bail, Result}; -use log::debug; use std::{env, path::PathBuf}; fn expand_glob_paths(path: PathBuf) -> Result> { @@ -15,45 +14,6 @@ fn expand_glob_paths(path: PathBuf) -> Result> { Ok(data_paths) } -#[cfg(target_os = "windows")] -pub fn expand_path(path: &str) -> Result { - use regex::Regex; - // Define a regex pattern to match placeholders like %SOMETHING% - let re = Regex::new(r"%([^%]+)%")?; - - // Clone the input path for modification - let mut expanded_path = path.to_owned(); - - // Iterate over all matches of the regex pattern in the input path - for capture in re.captures_iter(&path) { - // Get the matched placeholder (e.g., "APPDATA" from "%APPDATA%") - let placeholder = &capture[1]; - - // Try to get the corresponding environment variable value - if let Ok(var_value) = env::var(placeholder) { - // Replace the placeholder with the environment variable value - expanded_path = expanded_path.replace(&capture[0], &var_value); - } - } - - // Convert the expanded path to a PathBuf - let path_buf = PathBuf::from(expanded_path); - - Ok(path_buf) -} - -#[cfg(unix)] -pub fn expand_path(path: &str) -> Result { - // Get the value of the HOME environment variable - let home = env::var("HOME")?; - - // Replace ~ or $HOME with the actual home directory path - let expanded_path = path.replace('~', &home).replace("$HOME", &home); - - // Convert the expanded path to a PathBuf - Ok(PathBuf::from(expanded_path)) -} - pub fn find_chrome_based_paths(browser_config: &BrowserConfig) -> Result<(PathBuf, PathBuf)> { for path in browser_config.data_paths { // base paths @@ -72,7 +32,7 @@ pub fn find_chrome_based_paths(browser_config: &BrowserConfig) -> Result<(PathBu .map(|p| parent.join(p)) .find(|p| p.exists()) .unwrap_or_else(|| parent.join("Local State")); - debug!( + log::debug!( "Found chrome path {}, {}", db_path.display(), key_path.display() @@ -102,7 +62,7 @@ pub fn find_mozilla_based_paths(browser_config: &BrowserConfig) -> Result Result Result { for path in glob_paths { // expanded glob paths if path.exists() { - debug!("Found IE path {}", path.display()); + log::debug!("Found IE path {}", path.display()); return Ok(path); } } @@ -157,3 +117,41 @@ pub fn find_ie_based_paths(browser_config: &BrowserConfig) -> Result { bail!("Can't find any IE cookies file") } +#[cfg(target_os = "windows")] +pub fn expand_path(path: &str) -> Result { + use regex::Regex; + // Define a regex pattern to match placeholders like %SOMETHING% + let re = Regex::new(r"%([^%]+)%")?; + + // Clone the input path for modification + let mut expanded_path = path.to_owned(); + + // Iterate over all matches of the regex pattern in the input path + for capture in re.captures_iter(&path) { + // Get the matched placeholder (e.g., "APPDATA" from "%APPDATA%") + let placeholder = &capture[1]; + + // Try to get the corresponding environment variable value + if let Ok(var_value) = env::var(placeholder) { + // Replace the placeholder with the environment variable value + expanded_path = expanded_path.replace(&capture[0], &var_value); + } + } + + // Convert the expanded path to a PathBuf + let path_buf = PathBuf::from(expanded_path); + + Ok(path_buf) +} + +#[cfg(unix)] +pub fn expand_path(path: &str) -> Result { + // Get the value of the HOME environment variable + let home = env::var("HOME")?; + + // Replace ~ or $HOME with the actual home directory path + let expanded_path = path.replace('~', &home).replace("$HOME", &home); + + // Convert the expanded path to a PathBuf + Ok(PathBuf::from(expanded_path)) +} diff --git a/rookie-rs/src/common/secrets.rs b/rookie-rs/src/common/secrets.rs deleted file mode 100644 index 9cf6e31..0000000 --- a/rookie-rs/src/common/secrets.rs +++ /dev/null @@ -1,152 +0,0 @@ -use cfg_if::cfg_if; -#[cfg(unix)] -use eyre::{anyhow, bail, Result}; - -cfg_if! { - if #[cfg(target_os = "linux")] { - use crate::common::utils; - use crate::config; - use std::{ collections::HashMap, sync::Arc }; - use zbus::{ blocking::Connection, zvariant::Value, zvariant::ObjectPath, Message }; - - fn libsecret_call( - connection: &Connection, - method: &str, - args: T - ) -> zbus::Result> - where T: serde::ser::Serialize + zvariant::DynamicType - { - connection.call_method( - Some("org.freedesktop.secrets"), - "/org/freedesktop/secrets", - Some("org.freedesktop.Secret.Service"), - method, - &args - ) - } - - fn kwallet_call( - connection: &Connection, - method: &str, - args: T - ) -> zbus::Result> - where T: serde::ser::Serialize + zvariant::DynamicType - { - connection.call_method( - Some("org.kde.kwalletd5"), - "/modules/kwalletd5", - Some("org.kde.KWallet"), - method, - &args - ) - } - - pub fn get_passwords(os_crypt_name: &str) -> Result> { - // Attempt to get the password from libsecret - let mut passwords: Vec = vec![]; - for schema in [ - "chrome_libsecret_os_crypt_password_v2", - "chrome_libsecret_os_crypt_password_v1", - ] { - if let Ok(libsecret_pass) = get_password_libsecret(schema, os_crypt_name) { - passwords.push(libsecret_pass); - } - } - // Attempt to get the password from kdewallet - if let Ok(password) = get_password_kdewallet(os_crypt_name) { - passwords.push(password); - } - - Ok(passwords) - } - - fn get_password_libsecret(schema: &str, crypt_name: &str) -> Result { - let connection = Connection::session()?; - let mut content = HashMap::<&str, &str>::new(); - content.insert("xdg:schema", schema); - content.insert("application", crypt_name); - let m = libsecret_call(&connection, "SearchItems", &content)?; - let (reply_paths, _): (Vec, Vec) = m.body()?; - let path = reply_paths.first().ok_or(anyhow!("search items empty"))?; - - let m = libsecret_call(&connection, "Unlock", vec![path])?; - let reply: (Vec, ObjectPath) = m.body()?; - let object_path = reply.0.first().ok_or(anyhow!("Can't unlock"))?; - - let mut content = HashMap::<&str, &str>::new(); - content.insert("plain", ""); - let m = libsecret_call(&connection, "OpenSession", &("plain", Value::new("")))?; - - let reply: (Value, ObjectPath) = m.body()?; - let session = reply.1; - - let m = libsecret_call(&connection, "GetSecrets", &(vec![object_path], session))?; - type Response<'a> = (ObjectPath<'a>, Vec, Vec, String); - let reply: HashMap = m.body()?; - let inner = reply.get(object_path).ok_or(anyhow!("Can't get secrets"))?; - let secret = &inner.2; - - Ok(String::from_utf8(secret.clone())?) - } - - fn get_password_kdewallet(crypt_name: &str) -> Result { - let connection = Connection::session()?; - let folder = format!("{} Keys", utils::capitalize(crypt_name)); - let key = format!("{} Safe Storage", utils::capitalize(crypt_name)); - - let m = kwallet_call(&connection, "networkWallet", ())?; - let network_wallet: String = m.body()?; - - let m = kwallet_call(&connection, "open", ( - network_wallet.clone(), - 0_i64, - config::APP_ID, - ))?; - let handle: i32 = m.body()?; - let m = kwallet_call(&connection, "readPassword", ( - handle, - folder, - key, - config::APP_ID, - ))?; - let password: String = m.body()?; - let m = kwallet_call(&connection, "close", (network_wallet, false))?; - let close_ok: i32 = m.body()?; - if close_ok != 1 { - bail!("Close failed"); - } - - Ok(password) - } - } else if #[cfg(target_os = "macos")] { - use std::process::Command; - pub fn get_osx_keychain_password( - osx_key_service: &str, - osx_key_user: &str - ) -> Result { - let cmd = Command::new("/usr/bin/security") - .args([ - "-q", - "find-generic-password", - "-w", - "-a", - osx_key_user, - "-s", - osx_key_service, - ]) - .output(); - - match cmd { - Ok(output) => { - if output.status.success() { - let password = String::from_utf8(output.stdout)?; - Ok(password.trim().to_string()) - } else { - bail!("Failed to retrieve password from OSX Keychain") - } - } - Err(e) => Err(anyhow!("Error executing security command: {}", e)), - } - } - } -} diff --git a/rookie-rs/src/common/utils.rs b/rookie-rs/src/common/utils.rs index d7f50a5..2378e99 100644 --- a/rookie-rs/src/common/utils.rs +++ b/rookie-rs/src/common/utils.rs @@ -8,12 +8,3 @@ pub fn some_domain_in_host(domains: Option>, host: &str) -> bool { } false } - -#[cfg(target_os = "linux")] -pub fn capitalize(s: &str) -> String { - let mut c = s.chars(); - match c.next() { - None => String::new(), - Some(f) => f.to_uppercase().collect::() + c.as_str(), - } -} diff --git a/rookie-rs/src/config.rs b/rookie-rs/src/config.rs deleted file mode 100644 index 1ece841..0000000 --- a/rookie-rs/src/config.rs +++ /dev/null @@ -1,389 +0,0 @@ -use crate::common::enums::BrowserConfig; -use cfg_if::cfg_if; - -#[cfg(target_os = "linux")] -pub const APP_ID: &str = "rookie"; - -cfg_if! { - if #[cfg(target_os = "windows")] { - // Initialize the CHROME_CONFIG as a static variable with specific values - pub static CHROME_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "%LOCALAPPDATA%/Google/Chrome{channel}/User Data/Default/Cookies", - "%LOCALAPPDATA%/Google/Chrome{channel}/User Data/Default/Network/Cookies", - "%LOCALAPPDATA%/Google/Chrome{channel}/User Data/Profile */Cookies", - "%LOCALAPPDATA%/Google/Chrome{channel}/User Data/Profile */Network/Cookies", - - "%APPDATA%/Google/Chrome{channel}/User Data/Default/Cookies", - "%APPDATA%/Google/Chrome{channel}/User Data/Default/Network/Cookies", - "%APPDATA%/Google/Chrome{channel}/User Data/Profile */Cookies", - "%APPDATA%/Google/Chrome{channel}/User Data/Profile */Network/Cookies", - ], - channels: Some(&["", "-beta", "-dev", "-nightly"]), - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static BRAVE_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "%LOCALAPPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Default/Cookies", - "%LOCALAPPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Default/Network/Cookies", - "%LOCALAPPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Profile */Cookies", - "%LOCALAPPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Profile */Network/Cookies", - - "%APPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Default/Cookies", - "%APPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Default/Network/Cookies", - "%APPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Profile */Cookies", - "%APPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Profile */Network/Cookies", - ], - channels: Some(&["", "-beta", "-dev", "-nightly"]), - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static EDGE_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "%LOCALAPPDATA%/Microsoft/Edge{channel}/User Data/Default/Cookies", - "%LOCALAPPDATA%/Microsoft/Edge{channel}/User Data/Default/Network/Cookies", - "%LOCALAPPDATA%/Microsoft/Edge{channel}/User Data/Profile */Cookies", - "%LOCALAPPDATA%/Microsoft/Edge{channel}/User Data/Profile */Network/Cookies", - - "%APPDATA%/Microsoft/Edge{channel}/User Data/Default/Cookies", - "%APPDATA%/Microsoft/Edge{channel}/User Data/Default/Network/Cookies", - "%APPDATA%/Microsoft/Edge{channel}/User Data/Profile */Cookies", - "%APPDATA%/Microsoft/Edge{channel}/User Data/Profile */Network/Cookies", - ], - channels: Some(&["", "-beta", "-dev", "-nightly"]), - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static FIREFOX_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &["%APPDATA%/Mozilla/Firefox", "%LOCALAPPDATA%/Mozilla/Firefox"], - channels: Some(&["", "-beta", "-dev", "-nightly"]), - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static VIVALDI_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "%LOCALAPPDATA%/Vivaldi/User Data/Default/Cookies", - "%LOCALAPPDATA%/Vivaldi/User Data/Default/Network/Cookies", - "%LOCALAPPDATA%/Vivaldi/User Data/Profile */Cookies", - "%LOCALAPPDATA%/Vivaldi/User Data/Profile */Network/Cookies", - - "%APPDATA%/Vivaldi/User Data/Default/Cookies", - "%APPDATA%/Vivaldi/User Data/Default/Network/Cookies", - "%APPDATA%/Vivaldi/User Data/Profile */Cookies", - "%APPDATA%/Vivaldi/User Data/Profile */Network/Cookies", - ], - channels: None, - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static OPERA_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "%LOCALAPPDATA%/Opera Software/Opera {channel}/Cookies", - "%LOCALAPPDATA%/Opera Software/Opera {channel}/Network/Cookies", - - "%APPDATA%/Opera Software/Opera {channel}/Cookies", - "%APPDATA%/Opera Software/Opera {channel}/Network/Cookies", - ], - channels: Some(&["Stable", "Next", "Developer"]), - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static CHROMIUM_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "%LOCALAPPDATA%/Chromium/User Data/Default/Cookies", - "%LOCALAPPDATA%/Chromium/User Data/Default/Network/Cookies", - "%LOCALAPPDATA%/Chromium/User Data/Profile */Cookies", - "%LOCALAPPDATA%/Chromium/User Data/Profile */Network/Cookies", - - "%APPDATA%/Chromium/User Data/Default/Cookies", - "%APPDATA%/Chromium/User Data/Default/Network/Cookies", - "%APPDATA%/Chromium/User Data/Profile */Cookies", - "%APPDATA%/Chromium/User Data/Profile */Network/Cookies", - ], - channels: None, - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static OPERA_GX_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "%LOCALAPPDATA%/Opera Software/Opera GX {channel}/Cookies", - "%LOCALAPPDATA%/Opera Software/Opera GX {channel}/Network/Cookies", - - "%APPDATA%/Opera Software/Opera GX {channel}/Cookies", - "%APPDATA%/Opera Software/Opera GX {channel}/Network/Cookies", - ], - channels: Some(&["Stable", ""]), - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static OCTO_BROWSER_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "%LOCALAPPDATA%/Octo Browser/tmp/*/Default/Network/Cookies", - "%APPDATA%/Octo Browser/tmp/*/Default/Network/Cookies", - ], - channels: Some(&["Stable", ""]), - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static LIBREWOLF_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &["%LOCALAPPDATA%/librewolf", "%APPDATA%/librewolf"], - channels: None, - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static IE_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "%APPDATA%/Microsoft/Windows/WebCache/WebCacheV01.dat", - "%LOCALAPPDATA%/Microsoft/Windows/WebCache/WebCacheV01.dat", - ], - channels: None, - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - } else if #[cfg(target_os = "linux")] { - pub static CHROME_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/.config/google-chrome{channel}/Default/Cookies", - "~/.config/google-chrome{channel}/Profile */Cookies", - "~/.var/app/com.google.Chrome/config/google-chrome{channel}/Default/Cookies", - "~/.var/app/com.google.Chrome/config/google-chrome{channel}/Profile */Cookies", - ], - channels: Some(&["", "-beta", "-dev", "-nightly"]), - os_crypt_name: Some("chrome"), - osx_key_service: None, - osx_key_user: None, - }; - - pub static BRAVE_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/snap/brave/*/.config/BraveSoftware/Brave-Browser/Default/Cookies", - "~/.config/BraveSoftware/Brave-Browser{channel}/Default/Cookies", - "~/.config/BraveSoftware/Brave-Browser{channel}/Profile */Cookies", - "~/.var/app/com.brave.Browser/config/BraveSoftware/Brave-Browser{channel}/Default/Cookies", - "~/.var/app/com.brave.Browser/config/BraveSoftware/Brave-Browser{channel}/Profile */Cookies", - ], - channels: Some(&["", "-beta", "-dev", "-nightly"]), - os_crypt_name: Some("brave"), - osx_key_service: None, - osx_key_user: None, - }; - - pub static EDGE_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/.config/microsoft-edge{channel}/Default/Cookies", - "~/.config/microsoft-edge{channel}/Profile */Cookies", - "~/.var/app/com.microsoft.Edge/config/microsoft-edge{channel}/Default/Cookies", - "~/.var/app/com.microsoft.Edge/config/microsoft-edge{channel}/Profile */Cookies", - ], - channels: Some(&["", "-beta", "-dev", "-nightly"]), - os_crypt_name: Some("chromium"), - osx_key_service: None, - osx_key_user: None, - }; - - pub static VIVALDI_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/.config/vivaldi/Default/Cookies", - "~/.config/vivaldi/Profile */Cookies", - "~/.config/vivaldi-snapshot/Default/Cookies", - "~/.config/vivaldi-snapshot/Profile */Cookies", - "~/.var/app/com.vivaldi.Vivaldi/config/vivaldi/Default/Cookies", - "~/.var/app/com.vivaldi.Vivaldi/config/vivaldi/Profile */Cookies", - ], - channels: None, - os_crypt_name: Some("chrome"), - osx_key_service: None, - osx_key_user: None, - }; - - pub static OPERA_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/snap/opera/*/.config/opera/Cookies", - "~/.config/opera/Cookies", - "~/.config/opera-beta/Cookies", - "~/.config/opera-developer/Cookies", - "~/.var/app/com.opera.Opera/config/opera/Cookies", - "~/.var/app/com.opera.Opera/config/opera-beta/Cookies", - "~/.var/app/com.opera.Opera/config/opera-developer/Cookies", - ], - channels: Some(&["Stable", "Next", "Developer"]), - os_crypt_name: Some("chromium"), - osx_key_service: None, - osx_key_user: None, - }; - - pub static CHROMIUM_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/snap/chromium/common/chromium/Default/Cookies", - "~/.config/chromium/Default/Cookies", - "~/.config/chromium/Profile */Cookies", - "~/.var/app/org.chromium.Chromium/config/chromium/Default/Cookies", - "~/.var/app/org.chromium.Chromium/config/chromium/Profile */Cookies", - ], - channels: None, - os_crypt_name: Some("chromium"), - osx_key_service: None, - osx_key_user: None, - }; - - pub static FIREFOX_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/snap/firefox/common/.mozilla/firefox", - "~/.mozilla/firefox", - "~/.var/app/org.mozilla.firefox/.mozilla/firefox", - ], - channels: None, - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static LIBREWOLF_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &["~/snap/librewolf/common/.librewolf", "~/.librewolf"], - channels: None, - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static CACHY_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &["~/.cachy"], - channels: None, - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static OPERA_GX_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[], - channels: Some(&["", ""]), - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - } else if #[cfg(target_os = "macos")] { - pub static CHROME_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/Library/Application Support/Google/Chrome{channel}/Default/Cookies", - "~/Library/Application Support/Google/Chrome{channel}/Profile */Cookies", - ], - channels: Some(&["", "-beta", "-dev", "-nightly"]), - os_crypt_name: Some("chrome"), - osx_key_service: Some("Chrome Safe Storage"), - osx_key_user: Some("Chrome"), - }; - - pub static BRAVE_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/Library/Application Support/BraveSoftware/Brave-Browser{channel}/Default/Cookies", - "~/Library/Application Support/BraveSoftware/Brave-Browser{channel}/Profile */Cookies", - ], - channels: Some(&["", "-beta", "-dev", "-nightly"]), - os_crypt_name: Some("brave"), - osx_key_service: Some("Brave Safe Storage"), - osx_key_user: Some("Brave"), - }; - - pub static EDGE_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/Library/Application Support/Microsoft Edge{channel}/Default/Cookies", - "~/Library/Application Support/Microsoft Edge{channel}/Profile */Cookies", - ], - channels: Some(&["", " Beta", " Dev", " Canary"]), - os_crypt_name: Some("chromium"), - osx_key_service: Some("Microsoft Edge Safe Storage"), - osx_key_user: Some("Microsoft Edge"), - }; - - pub static FIREFOX_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &["~/Library/Application Support/Firefox"], - channels: None, - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static LIBREWOLF_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &["~/Library/Application Support/librewolf"], - channels: None, - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - - pub static VIVALDI_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/Library/Application Support/Vivaldi/Default/Cookies", - "~/Library/Application Support/Vivaldi/Profile */Cookies", - ], - channels: None, - os_crypt_name: Some("chrome"), - osx_key_service: Some("Vivaldi Safe Storage"), - osx_key_user: Some("Vivaldi"), - }; - - pub static OPERA_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/Library/Application Support/com.operasoftware.Opera/Cookies", - "~/Library/Application Support/com.operasoftware.OperaNext/Cookies", - "~/Library/Application Support/com.operasoftware.OperaDeveloper/Cookies", - ], - channels: Some(&["Stable", "Next", "Developer"]), - os_crypt_name: Some("chromium"), - osx_key_service: Some("Opera Safe Storage"), - osx_key_user: Some("Opera"), - }; - - pub static CHROMIUM_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/Library/Application Support/Chromium/Default/Cookies", - "~/Library/Application Support/Chromium/Profile */Cookies", - ], - channels: None, - os_crypt_name: Some("chromium"), - osx_key_service: Some("Chromium Safe Storage"), - osx_key_user: Some("Chromium"), - }; - - pub static OPERA_GX_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &["~/Library/Application Support/com.operasoftware.OperaGX/Cookies"], - channels: Some(&["Stable", ""]), - os_crypt_name: Some("chromium"), - osx_key_service: Some("Opera Safe Storage"), - osx_key_user: Some("Opera"), - }; - - pub static SAFARI_CONFIG: BrowserConfig<'static> = BrowserConfig { - data_paths: &[ - "~/Library/Containers/com.apple.Safari/Data/Library/Cookies/Cookies.binarycookies", - "~/Library/Cookies/Cookies.binarycookies", - ], - channels: None, - os_crypt_name: None, - osx_key_service: None, - osx_key_user: None, - }; - } // not available on Linux -} diff --git a/rookie-rs/src/lib.rs b/rookie-rs/src/lib.rs index 6812897..9f22015 100644 --- a/rookie-rs/src/lib.rs +++ b/rookie-rs/src/lib.rs @@ -1,23 +1,40 @@ pub mod browser; pub mod common; -pub mod config; use browser::{chromium::chromium_based, mozilla::firefox_based}; use common::{enums::Cookie, paths}; use eyre::{bail, Result}; +// MacOS +#[cfg(target_os = "macos")] +pub mod macos; + #[cfg(target_os = "macos")] use browser::safari::safari_based; +#[cfg(target_os = "macos")] +pub use macos::config; + +// Windows #[cfg(target_os = "windows")] -use browser::internet_explorer; -#[cfg(target_os = "windows")] -use common::winapi; +pub mod windows; + #[cfg(target_os = "windows")] -pub use internet_explorer::internet_explorer_based; +pub use browser::internet_explorer; + #[cfg(target_os = "windows")] use std::path::PathBuf; +#[cfg(target_os = "windows")] +pub use windows::config; + +// Linux +#[cfg(target_os = "linux")] +pub mod linux; + +#[cfg(target_os = "linux")] +pub use linux::config; + /// Returns cookies from Firefox /// /// # Arguments diff --git a/rookie-rs/src/linux/config.rs b/rookie-rs/src/linux/config.rs new file mode 100644 index 0000000..980f6c3 --- /dev/null +++ b/rookie-rs/src/linux/config.rs @@ -0,0 +1,122 @@ +use crate::common::enums::BrowserConfig; + +pub static CHROME_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/.config/google-chrome{channel}/Default/Cookies", + "~/.config/google-chrome{channel}/Profile */Cookies", + "~/.var/app/com.google.Chrome/config/google-chrome{channel}/Default/Cookies", + "~/.var/app/com.google.Chrome/config/google-chrome{channel}/Profile */Cookies", + ], + channels: Some(&["", "-beta", "-dev", "-nightly"]), + os_crypt_name: Some("chrome"), + osx_key_service: None, + osx_key_user: None, +}; + +pub static BRAVE_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/snap/brave/*/.config/BraveSoftware/Brave-Browser/Default/Cookies", + "~/.config/BraveSoftware/Brave-Browser{channel}/Default/Cookies", + "~/.config/BraveSoftware/Brave-Browser{channel}/Profile */Cookies", + "~/.var/app/com.brave.Browser/config/BraveSoftware/Brave-Browser{channel}/Default/Cookies", + "~/.var/app/com.brave.Browser/config/BraveSoftware/Brave-Browser{channel}/Profile */Cookies", + ], + channels: Some(&["", "-beta", "-dev", "-nightly"]), + os_crypt_name: Some("brave"), + osx_key_service: None, + osx_key_user: None, +}; + +pub static EDGE_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/.config/microsoft-edge{channel}/Default/Cookies", + "~/.config/microsoft-edge{channel}/Profile */Cookies", + "~/.var/app/com.microsoft.Edge/config/microsoft-edge{channel}/Default/Cookies", + "~/.var/app/com.microsoft.Edge/config/microsoft-edge{channel}/Profile */Cookies", + ], + channels: Some(&["", "-beta", "-dev", "-nightly"]), + os_crypt_name: Some("chromium"), + osx_key_service: None, + osx_key_user: None, +}; + +pub static VIVALDI_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/.config/vivaldi/Default/Cookies", + "~/.config/vivaldi/Profile */Cookies", + "~/.config/vivaldi-snapshot/Default/Cookies", + "~/.config/vivaldi-snapshot/Profile */Cookies", + "~/.var/app/com.vivaldi.Vivaldi/config/vivaldi/Default/Cookies", + "~/.var/app/com.vivaldi.Vivaldi/config/vivaldi/Profile */Cookies", + ], + channels: None, + os_crypt_name: Some("chrome"), + osx_key_service: None, + osx_key_user: None, +}; + +pub static OPERA_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/snap/opera/*/.config/opera/Cookies", + "~/.config/opera/Cookies", + "~/.config/opera-beta/Cookies", + "~/.config/opera-developer/Cookies", + "~/.var/app/com.opera.Opera/config/opera/Cookies", + "~/.var/app/com.opera.Opera/config/opera-beta/Cookies", + "~/.var/app/com.opera.Opera/config/opera-developer/Cookies", + ], + channels: Some(&["Stable", "Next", "Developer"]), + os_crypt_name: Some("chromium"), + osx_key_service: None, + osx_key_user: None, +}; + +pub static CHROMIUM_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/snap/chromium/common/chromium/Default/Cookies", + "~/.config/chromium/Default/Cookies", + "~/.config/chromium/Profile */Cookies", + "~/.var/app/org.chromium.Chromium/config/chromium/Default/Cookies", + "~/.var/app/org.chromium.Chromium/config/chromium/Profile */Cookies", + ], + channels: None, + os_crypt_name: Some("chromium"), + osx_key_service: None, + osx_key_user: None, +}; + +pub static FIREFOX_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/snap/firefox/common/.mozilla/firefox", + "~/.mozilla/firefox", + "~/.var/app/org.mozilla.firefox/.mozilla/firefox", + ], + channels: None, + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static LIBREWOLF_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &["~/snap/librewolf/common/.librewolf", "~/.librewolf"], + channels: None, + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static CACHY_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &["~/.cachy"], + channels: None, + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static OPERA_GX_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[], + channels: Some(&["", ""]), + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; diff --git a/rookie-rs/src/linux/mod.rs b/rookie-rs/src/linux/mod.rs new file mode 100644 index 0000000..1828d6a --- /dev/null +++ b/rookie-rs/src/linux/mod.rs @@ -0,0 +1,5 @@ +mod config; +pub mod path; +pub mod secrets; + +pub const APP_ID: &str = "rookie"; diff --git a/rookie-rs/src/linux/secrets.rs b/rookie-rs/src/linux/secrets.rs new file mode 100644 index 0000000..6bcea73 --- /dev/null +++ b/rookie-rs/src/linux/secrets.rs @@ -0,0 +1,119 @@ +#[cfg(unix)] +use eyre::{anyhow, bail, Result}; + +use crate::common::utils; +use crate::config; +use std::{collections::HashMap, sync::Arc}; +use zbus::{blocking::Connection, zvariant::ObjectPath, zvariant::Value, Message}; + +/// Get password from either kdewallet or libsecret (ubuntu) +pub fn get_passwords(os_crypt_name: &str) -> Result> { + // Attempt to get the password from libsecret + let mut passwords: Vec = vec![]; + for schema in [ + "chrome_libsecret_os_crypt_password_v2", + "chrome_libsecret_os_crypt_password_v1", + ] { + if let Ok(libsecret_pass) = get_password_libsecret(schema, os_crypt_name) { + passwords.push(libsecret_pass); + } + } + // Attempt to get the password from kdewallet + if let Ok(password) = get_password_kdewallet(os_crypt_name) { + passwords.push(password); + } + + Ok(passwords) +} + +fn libsecret_call(connection: &Connection, method: &str, args: T) -> zbus::Result> +where + T: serde::ser::Serialize + zvariant::DynamicType, +{ + connection.call_method( + Some("org.freedesktop.secrets"), + "/org/freedesktop/secrets", + Some("org.freedesktop.Secret.Service"), + method, + &args, + ) +} + +fn kwallet_call(connection: &Connection, method: &str, args: T) -> zbus::Result> +where + T: serde::ser::Serialize + zvariant::DynamicType, +{ + connection.call_method( + Some("org.kde.kwalletd5"), + "/modules/kwalletd5", + Some("org.kde.KWallet"), + method, + &args, + ) +} + +fn get_password_libsecret(schema: &str, crypt_name: &str) -> Result { + let connection = Connection::session()?; + let mut content = HashMap::<&str, &str>::new(); + content.insert("xdg:schema", schema); + content.insert("application", crypt_name); + let m = libsecret_call(&connection, "SearchItems", &content)?; + let (reply_paths, _): (Vec, Vec) = m.body()?; + let path = reply_paths.first().ok_or(anyhow!("search items empty"))?; + + let m = libsecret_call(&connection, "Unlock", vec![path])?; + let reply: (Vec, ObjectPath) = m.body()?; + let object_path = reply.0.first().ok_or(anyhow!("Can't unlock"))?; + + let mut content = HashMap::<&str, &str>::new(); + content.insert("plain", ""); + let m = libsecret_call(&connection, "OpenSession", &("plain", Value::new("")))?; + + let reply: (Value, ObjectPath) = m.body()?; + let session = reply.1; + + let m = libsecret_call(&connection, "GetSecrets", &(vec![object_path], session))?; + type Response<'a> = (ObjectPath<'a>, Vec, Vec, String); + let reply: HashMap = m.body()?; + let inner = reply.get(object_path).ok_or(anyhow!("Can't get secrets"))?; + let secret = &inner.2; + + Ok(String::from_utf8(secret.clone())?) +} + +fn get_password_kdewallet(crypt_name: &str) -> Result { + let connection = Connection::session()?; + let folder = format!("{} Keys", utils::capitalize(crypt_name)); + let key = format!("{} Safe Storage", utils::capitalize(crypt_name)); + + let m = kwallet_call(&connection, "networkWallet", ())?; + let network_wallet: String = m.body()?; + + let m = kwallet_call( + &connection, + "open", + (network_wallet.clone(), 0_i64, config::APP_ID), + )?; + let handle: i32 = m.body()?; + let m = kwallet_call( + &connection, + "readPassword", + (handle, folder, key, config::APP_ID), + )?; + let password: String = m.body()?; + let m = kwallet_call(&connection, "close", (network_wallet, false))?; + let close_ok: i32 = m.body()?; + if close_ok != 1 { + bail!("Close failed"); + } + + Ok(password) +} + +pub fn capitalize(s: &str) -> String { + let mut c = s.chars(); + match c.next() { + None => String::new(), + Some(f) => f.to_uppercase().collect::() + c.as_str(), + } +} diff --git a/rookie-rs/src/macos/config.rs b/rookie-rs/src/macos/config.rs new file mode 100644 index 0000000..bf2fe34 --- /dev/null +++ b/rookie-rs/src/macos/config.rs @@ -0,0 +1,103 @@ +use crate::common::enums::BrowserConfig; + +pub static CHROME_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/Library/Application Support/Google/Chrome{channel}/Default/Cookies", + "~/Library/Application Support/Google/Chrome{channel}/Profile */Cookies", + ], + channels: Some(&["", "-beta", "-dev", "-nightly"]), + os_crypt_name: Some("chrome"), + osx_key_service: Some("Chrome Safe Storage"), + osx_key_user: Some("Chrome"), +}; + +pub static BRAVE_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/Library/Application Support/BraveSoftware/Brave-Browser{channel}/Default/Cookies", + "~/Library/Application Support/BraveSoftware/Brave-Browser{channel}/Profile */Cookies", + ], + channels: Some(&["", "-beta", "-dev", "-nightly"]), + os_crypt_name: Some("brave"), + osx_key_service: Some("Brave Safe Storage"), + osx_key_user: Some("Brave"), +}; + +pub static EDGE_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/Library/Application Support/Microsoft Edge{channel}/Default/Cookies", + "~/Library/Application Support/Microsoft Edge{channel}/Profile */Cookies", + ], + channels: Some(&["", " Beta", " Dev", " Canary"]), + os_crypt_name: Some("chromium"), + osx_key_service: Some("Microsoft Edge Safe Storage"), + osx_key_user: Some("Microsoft Edge"), +}; + +pub static FIREFOX_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &["~/Library/Application Support/Firefox"], + channels: None, + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static LIBREWOLF_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &["~/Library/Application Support/librewolf"], + channels: None, + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static VIVALDI_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/Library/Application Support/Vivaldi/Default/Cookies", + "~/Library/Application Support/Vivaldi/Profile */Cookies", + ], + channels: None, + os_crypt_name: Some("chrome"), + osx_key_service: Some("Vivaldi Safe Storage"), + osx_key_user: Some("Vivaldi"), +}; + +pub static OPERA_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/Library/Application Support/com.operasoftware.Opera/Cookies", + "~/Library/Application Support/com.operasoftware.OperaNext/Cookies", + "~/Library/Application Support/com.operasoftware.OperaDeveloper/Cookies", + ], + channels: Some(&["Stable", "Next", "Developer"]), + os_crypt_name: Some("chromium"), + osx_key_service: Some("Opera Safe Storage"), + osx_key_user: Some("Opera"), +}; + +pub static CHROMIUM_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/Library/Application Support/Chromium/Default/Cookies", + "~/Library/Application Support/Chromium/Profile */Cookies", + ], + channels: None, + os_crypt_name: Some("chromium"), + osx_key_service: Some("Chromium Safe Storage"), + osx_key_user: Some("Chromium"), +}; + +pub static OPERA_GX_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &["~/Library/Application Support/com.operasoftware.OperaGX/Cookies"], + channels: Some(&["Stable", ""]), + os_crypt_name: Some("chromium"), + osx_key_service: Some("Opera Safe Storage"), + osx_key_user: Some("Opera"), +}; + +pub static SAFARI_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "~/Library/Containers/com.apple.Safari/Data/Library/Cookies/Cookies.binarycookies", + "~/Library/Cookies/Cookies.binarycookies", + ], + channels: None, + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; diff --git a/rookie-rs/src/macos/mod.rs b/rookie-rs/src/macos/mod.rs new file mode 100644 index 0000000..d62d7c1 --- /dev/null +++ b/rookie-rs/src/macos/mod.rs @@ -0,0 +1,2 @@ +pub mod config; +pub mod secrets; diff --git a/rookie-rs/src/macos/secrets.rs b/rookie-rs/src/macos/secrets.rs new file mode 100644 index 0000000..4c4afc8 --- /dev/null +++ b/rookie-rs/src/macos/secrets.rs @@ -0,0 +1,29 @@ +#[cfg(unix)] +use eyre::{anyhow, bail, Result}; + +use std::process::Command; +pub fn get_osx_keychain_password(osx_key_service: &str, osx_key_user: &str) -> Result { + let cmd = Command::new("/usr/bin/security") + .args([ + "-q", + "find-generic-password", + "-w", + "-a", + osx_key_user, + "-s", + osx_key_service, + ]) + .output(); + + match cmd { + Ok(output) => { + if output.status.success() { + let password = String::from_utf8(output.stdout)?; + Ok(password.trim().to_string()) + } else { + bail!("Failed to retrieve password from OSX Keychain") + } + } + Err(e) => Err(anyhow!("Error executing security command: {}", e)), + } +} diff --git a/rookie-rs/src/windows/config.rs b/rookie-rs/src/windows/config.rs new file mode 100644 index 0000000..77f286c --- /dev/null +++ b/rookie-rs/src/windows/config.rs @@ -0,0 +1,154 @@ +use crate::common::enums::BrowserConfig; + +// Initialize the CHROME_CONFIG as a static variable with specific values +pub static CHROME_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "%LOCALAPPDATA%/Google/Chrome{channel}/User Data/Default/Cookies", + "%LOCALAPPDATA%/Google/Chrome{channel}/User Data/Default/Network/Cookies", + "%LOCALAPPDATA%/Google/Chrome{channel}/User Data/Profile */Cookies", + "%LOCALAPPDATA%/Google/Chrome{channel}/User Data/Profile */Network/Cookies", + "%APPDATA%/Google/Chrome{channel}/User Data/Default/Cookies", + "%APPDATA%/Google/Chrome{channel}/User Data/Default/Network/Cookies", + "%APPDATA%/Google/Chrome{channel}/User Data/Profile */Cookies", + "%APPDATA%/Google/Chrome{channel}/User Data/Profile */Network/Cookies", + ], + channels: Some(&["", "-beta", "-dev", "-nightly"]), + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static BRAVE_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "%LOCALAPPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Default/Cookies", + "%LOCALAPPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Default/Network/Cookies", + "%LOCALAPPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Profile */Cookies", + "%LOCALAPPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Profile */Network/Cookies", + "%APPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Default/Cookies", + "%APPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Default/Network/Cookies", + "%APPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Profile */Cookies", + "%APPDATA%/BraveSoftware/Brave-Browser{channel}/User Data/Profile */Network/Cookies", + ], + channels: Some(&["", "-beta", "-dev", "-nightly"]), + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static EDGE_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "%LOCALAPPDATA%/Microsoft/Edge{channel}/User Data/Default/Cookies", + "%LOCALAPPDATA%/Microsoft/Edge{channel}/User Data/Default/Network/Cookies", + "%LOCALAPPDATA%/Microsoft/Edge{channel}/User Data/Profile */Cookies", + "%LOCALAPPDATA%/Microsoft/Edge{channel}/User Data/Profile */Network/Cookies", + "%APPDATA%/Microsoft/Edge{channel}/User Data/Default/Cookies", + "%APPDATA%/Microsoft/Edge{channel}/User Data/Default/Network/Cookies", + "%APPDATA%/Microsoft/Edge{channel}/User Data/Profile */Cookies", + "%APPDATA%/Microsoft/Edge{channel}/User Data/Profile */Network/Cookies", + ], + channels: Some(&["", "-beta", "-dev", "-nightly"]), + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static FIREFOX_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "%APPDATA%/Mozilla/Firefox", + "%LOCALAPPDATA%/Mozilla/Firefox", + ], + channels: Some(&["", "-beta", "-dev", "-nightly"]), + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static VIVALDI_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "%LOCALAPPDATA%/Vivaldi/User Data/Default/Cookies", + "%LOCALAPPDATA%/Vivaldi/User Data/Default/Network/Cookies", + "%LOCALAPPDATA%/Vivaldi/User Data/Profile */Cookies", + "%LOCALAPPDATA%/Vivaldi/User Data/Profile */Network/Cookies", + "%APPDATA%/Vivaldi/User Data/Default/Cookies", + "%APPDATA%/Vivaldi/User Data/Default/Network/Cookies", + "%APPDATA%/Vivaldi/User Data/Profile */Cookies", + "%APPDATA%/Vivaldi/User Data/Profile */Network/Cookies", + ], + channels: None, + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static OPERA_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "%LOCALAPPDATA%/Opera Software/Opera {channel}/Cookies", + "%LOCALAPPDATA%/Opera Software/Opera {channel}/Network/Cookies", + "%APPDATA%/Opera Software/Opera {channel}/Cookies", + "%APPDATA%/Opera Software/Opera {channel}/Network/Cookies", + ], + channels: Some(&["Stable", "Next", "Developer"]), + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static CHROMIUM_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "%LOCALAPPDATA%/Chromium/User Data/Default/Cookies", + "%LOCALAPPDATA%/Chromium/User Data/Default/Network/Cookies", + "%LOCALAPPDATA%/Chromium/User Data/Profile */Cookies", + "%LOCALAPPDATA%/Chromium/User Data/Profile */Network/Cookies", + "%APPDATA%/Chromium/User Data/Default/Cookies", + "%APPDATA%/Chromium/User Data/Default/Network/Cookies", + "%APPDATA%/Chromium/User Data/Profile */Cookies", + "%APPDATA%/Chromium/User Data/Profile */Network/Cookies", + ], + channels: None, + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static OPERA_GX_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "%LOCALAPPDATA%/Opera Software/Opera GX {channel}/Cookies", + "%LOCALAPPDATA%/Opera Software/Opera GX {channel}/Network/Cookies", + "%APPDATA%/Opera Software/Opera GX {channel}/Cookies", + "%APPDATA%/Opera Software/Opera GX {channel}/Network/Cookies", + ], + channels: Some(&["Stable", ""]), + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static OCTO_BROWSER_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "%LOCALAPPDATA%/Octo Browser/tmp/*/Default/Network/Cookies", + "%APPDATA%/Octo Browser/tmp/*/Default/Network/Cookies", + ], + channels: Some(&["Stable", ""]), + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static LIBREWOLF_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &["%LOCALAPPDATA%/librewolf", "%APPDATA%/librewolf"], + channels: None, + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; + +pub static IE_CONFIG: BrowserConfig<'static> = BrowserConfig { + data_paths: &[ + "%APPDATA%/Microsoft/Windows/WebCache/WebCacheV01.dat", + "%LOCALAPPDATA%/Microsoft/Windows/WebCache/WebCacheV01.dat", + ], + channels: None, + os_crypt_name: None, + osx_key_service: None, + osx_key_user: None, +}; diff --git a/rookie-rs/src/common/winapi.rs b/rookie-rs/src/windows/dpapi.rs similarity index 100% rename from rookie-rs/src/common/winapi.rs rename to rookie-rs/src/windows/dpapi.rs diff --git a/rookie-rs/src/windows/file_unlock.rs b/rookie-rs/src/windows/file_unlock.rs new file mode 100644 index 0000000..7d4b433 --- /dev/null +++ b/rookie-rs/src/windows/file_unlock.rs @@ -0,0 +1,60 @@ +use std::{ffi::c_void, ptr}; + +use eyre::{anyhow, bail, Result}; +use windows::{ + core::{HSTRING, PCWSTR, PWSTR}, + Win32::{ + Foundation, + Foundation::{ERROR_MORE_DATA, ERROR_SUCCESS, WIN32_ERROR}, + Security::Cryptography, + System::RestartManager::{ + RmEndSession, RmForceShutdown, RmGetList, RmRegisterResources, RmShutdown, RmStartSession, + CCH_RM_SESSION_KEY, RM_PROCESS_INFO, + }, + }, +}; + +pub unsafe fn release_file_lock(file_path: &str) -> bool { + let file_path = HSTRING::from(file_path); + let mut session: u32 = 0; + let mut session_key_buffer = [0_u16; (CCH_RM_SESSION_KEY as usize) + 1]; + let session_key = PWSTR(session_key_buffer.as_mut_ptr()); + let result = RmStartSession(&mut session, 0, session_key); + if WIN32_ERROR(result) == ERROR_SUCCESS { + let result = RmRegisterResources(session, Some(&[PCWSTR(file_path.as_ptr())]), None, None); + if WIN32_ERROR(result) == ERROR_SUCCESS { + let mut pnprocinfoneeded: u32 = 0; + let mut rgaffectedapps: [RM_PROCESS_INFO; 1] = [RM_PROCESS_INFO { + ..Default::default() + }]; + let mut lpdwrebootreasons: u32 = 0; + let mut pnprocinfo: u32 = 0; + let result = RmGetList( + session, + &mut pnprocinfoneeded, + &mut pnprocinfo, + Some(rgaffectedapps.as_mut_ptr()), + &mut lpdwrebootreasons, + ); + if WIN32_ERROR(result) == ERROR_SUCCESS || WIN32_ERROR(result) == ERROR_MORE_DATA { + if pnprocinfoneeded > 0 { + // If current process does not have enough privileges to close one of + // the "offending" processes, you'll get ERROR_FAIL_NOACTION_REBOOT + let result = RmShutdown(session, RmForceShutdown.0 as u32, None); + if WIN32_ERROR(result) == ERROR_SUCCESS { + // success + RmEndSession(session); + return true; + } + } else { + // success + RmEndSession(session); + return true; + } + } + } + RmEndSession(session); + return false; + } + return false; +} diff --git a/rookie-rs/src/windows/mod.rs b/rookie-rs/src/windows/mod.rs new file mode 100644 index 0000000..fe3b7a4 --- /dev/null +++ b/rookie-rs/src/windows/mod.rs @@ -0,0 +1,3 @@ +mod config; +mod file_unlock; +pub mod path; From a276ebcd41420a6154d8dcdfdca2c8e5ddfc8054 Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Sun, 21 Apr 2024 23:11:21 +0300 Subject: [PATCH 03/10] fix on linux --- rookie-rs/src/browser/chromium.rs | 4 ++-- rookie-rs/src/linux/mod.rs | 3 +-- rookie-rs/src/linux/secrets.rs | 22 ++++++---------------- 3 files changed, 9 insertions(+), 20 deletions(-) diff --git a/rookie-rs/src/browser/chromium.rs b/rookie-rs/src/browser/chromium.rs index 041a363..042843b 100644 --- a/rookie-rs/src/browser/chromium.rs +++ b/rookie-rs/src/browser/chromium.rs @@ -1,6 +1,6 @@ use crate::common::{date, enums::*, sqlite}; use cfg_if::cfg_if; -use eyre::{bail, ContextCompat, Result}; +use eyre::{bail, Result}; use std::path::PathBuf; #[cfg(target_os = "macos")] @@ -81,7 +81,7 @@ fn get_keys(config: &BrowserConfig) -> Result>> { iterations = 1; let mut keys: Vec> = vec![]; - if let Ok(passwords) = secrets::get_passwords(config.os_crypt_name.unwrap_or("")) { + if let Ok(passwords) = crate::linux::secrets::get_passwords(config.os_crypt_name.unwrap_or("")) { for password in passwords { let key = create_pbkdf2_key(password.as_str(), salt, iterations); keys.push(key); diff --git a/rookie-rs/src/linux/mod.rs b/rookie-rs/src/linux/mod.rs index 1828d6a..512c95f 100644 --- a/rookie-rs/src/linux/mod.rs +++ b/rookie-rs/src/linux/mod.rs @@ -1,5 +1,4 @@ -mod config; -pub mod path; +pub mod config; pub mod secrets; pub const APP_ID: &str = "rookie"; diff --git a/rookie-rs/src/linux/secrets.rs b/rookie-rs/src/linux/secrets.rs index 6bcea73..a9ab6c6 100644 --- a/rookie-rs/src/linux/secrets.rs +++ b/rookie-rs/src/linux/secrets.rs @@ -1,11 +1,9 @@ -#[cfg(unix)] use eyre::{anyhow, bail, Result}; - -use crate::common::utils; -use crate::config; use std::{collections::HashMap, sync::Arc}; use zbus::{blocking::Connection, zvariant::ObjectPath, zvariant::Value, Message}; +use super::APP_ID; + /// Get password from either kdewallet or libsecret (ubuntu) pub fn get_passwords(os_crypt_name: &str) -> Result> { // Attempt to get the password from libsecret @@ -83,23 +81,15 @@ fn get_password_libsecret(schema: &str, crypt_name: &str) -> Result { fn get_password_kdewallet(crypt_name: &str) -> Result { let connection = Connection::session()?; - let folder = format!("{} Keys", utils::capitalize(crypt_name)); - let key = format!("{} Safe Storage", utils::capitalize(crypt_name)); + let folder = format!("{} Keys", capitalize(crypt_name)); + let key = format!("{} Safe Storage", capitalize(crypt_name)); let m = kwallet_call(&connection, "networkWallet", ())?; let network_wallet: String = m.body()?; - let m = kwallet_call( - &connection, - "open", - (network_wallet.clone(), 0_i64, config::APP_ID), - )?; + let m = kwallet_call(&connection, "open", (network_wallet.clone(), 0_i64, APP_ID))?; let handle: i32 = m.body()?; - let m = kwallet_call( - &connection, - "readPassword", - (handle, folder, key, config::APP_ID), - )?; + let m = kwallet_call(&connection, "readPassword", (handle, folder, key, APP_ID))?; let password: String = m.body()?; let m = kwallet_call(&connection, "close", (network_wallet, false))?; let close_ok: i32 = m.body()?; From 2376a8795f71cb7ccf5e9a638c10ba10026b7cde Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Sun, 21 Apr 2024 23:12:45 +0300 Subject: [PATCH 04/10] fix clippy errors --- rookie-rs/Cargo.toml | 3 --- rookie-rs/src/browser/chromium.rs | 3 +-- rookie-rs/src/windows/mod.rs | 1 - 3 files changed, 1 insertion(+), 6 deletions(-) diff --git a/rookie-rs/Cargo.toml b/rookie-rs/Cargo.toml index 6f0f217..391b332 100644 --- a/rookie-rs/Cargo.toml +++ b/rookie-rs/Cargo.toml @@ -14,9 +14,6 @@ keywords = ["windows", "cookies", "rust", "web"] name = "rookie" path = "src/lib.rs" -[[bin]] -name = "main" -path = "bin/main.rs" [dependencies] aes = "0.8" diff --git a/rookie-rs/src/browser/chromium.rs b/rookie-rs/src/browser/chromium.rs index 042843b..e5b54dd 100644 --- a/rookie-rs/src/browser/chromium.rs +++ b/rookie-rs/src/browser/chromium.rs @@ -76,9 +76,8 @@ fn get_keys(config: &BrowserConfig) -> Result>> { // AES CBC key let salt = b"saltysalt"; - let iterations: u32; - iterations = 1; + let iterations = 1; let mut keys: Vec> = vec![]; if let Ok(passwords) = crate::linux::secrets::get_passwords(config.os_crypt_name.unwrap_or("")) { diff --git a/rookie-rs/src/windows/mod.rs b/rookie-rs/src/windows/mod.rs index fe3b7a4..da98f52 100644 --- a/rookie-rs/src/windows/mod.rs +++ b/rookie-rs/src/windows/mod.rs @@ -1,3 +1,2 @@ mod config; mod file_unlock; -pub mod path; From d84b9c977bce73335a3278680967af61f2ead698 Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Sun, 21 Apr 2024 23:15:36 +0300 Subject: [PATCH 05/10] update building --- BUILDING.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/BUILDING.md b/BUILDING.md index 7410c02..e5f14b2 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -1,5 +1,11 @@ # Build rookie +## Linux setup + +```console +sudo apt-get install -y python3-dev +``` + ## rookie-rs ```console From 20493e97a2ca97b89b345920c9cac197d263cd10 Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Sun, 21 Apr 2024 23:19:13 +0300 Subject: [PATCH 06/10] remove cfg if from deps --- rookie-rs/Cargo.toml | 1 - rookie-rs/src/browser/chromium.rs | 25 +++++++++++++------------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/rookie-rs/Cargo.toml b/rookie-rs/Cargo.toml index 391b332..8a4d0ac 100644 --- a/rookie-rs/Cargo.toml +++ b/rookie-rs/Cargo.toml @@ -20,7 +20,6 @@ aes = "0.8" aes-gcm = "0.10" byteorder = "1" cbc = "0.1" -cfg-if = "1" eyre = { version = "0.6.12", features = ["pyo3"] } glob = "0.3" log = "0.4" diff --git a/rookie-rs/src/browser/chromium.rs b/rookie-rs/src/browser/chromium.rs index e5b54dd..ce391a5 100644 --- a/rookie-rs/src/browser/chromium.rs +++ b/rookie-rs/src/browser/chromium.rs @@ -1,22 +1,23 @@ use crate::common::{date, enums::*, sqlite}; -use cfg_if::cfg_if; use eyre::{bail, Result}; use std::path::PathBuf; #[cfg(target_os = "macos")] use crate::macos::secrets; -cfg_if! { - if #[cfg(target_os = "windows")] { - use aes_gcm::{ Aes256Gcm, Key, aead::{ Aead, KeyInit, generic_array::GenericArray } }; - use serde_json; - use base64::{ Engine as _, engine::general_purpose }; - use crate::winapi; - use eyre::Context; - } else if #[cfg(unix)] { - - } -} +#[cfg(target_os = "windows")] +use crate::winapi; +#[cfg(target_os = "windows")] +use aes_gcm::{ + aead::{generic_array::GenericArray, Aead, KeyInit}, + Aes256Gcm, Key, +}; +#[cfg(target_os = "windows")] +use base64::{engine::general_purpose, Engine as _}; +#[cfg(target_os = "windows")] +use eyre::Context; +#[cfg(target_os = "windows")] +use serde_json; #[cfg(target_os = "windows")] pub fn chromium_based( From 7b5ec97c925303a4ae6ae3e9af11a20b24948532 Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Sun, 21 Apr 2024 21:19:14 +0300 Subject: [PATCH 07/10] windows refactor --- .vscode/settings.json | 6 +- bindings/python/src/browsers.rs | 8 +-- bindings/python/src/lib.rs | 2 +- rookie-rs/examples/simple.rs | 2 +- rookie-rs/src/browser/chromium.rs | 24 ++++---- rookie-rs/src/browser/internet_explorer.rs | 8 +-- rookie-rs/src/browser/mod.rs | 8 +-- rookie-rs/src/browser/mozilla.rs | 1 + rookie-rs/src/common/date.rs | 2 +- rookie-rs/src/common/mod.rs | 8 +-- rookie-rs/src/common/paths.rs | 6 +- rookie-rs/src/lib.rs | 64 +++++++++++----------- rookie-rs/src/windows/dpapi.rs | 6 +- rookie-rs/src/windows/file_unlock.rs | 7 +-- rookie-rs/src/windows/mod.rs | 5 +- 15 files changed, 74 insertions(+), 83 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index cbbe18f..9815343 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -2,6 +2,8 @@ "editor.formatOnSave": true, "editor.tabSize": 2, "[rust]": { - "editor.defaultFormatter": "rust-lang.rust-analyzer" - } + "editor.defaultFormatter": "rust-lang.rust-analyzer", + }, + "rust-analyzer.checkOnSave": true, + "rust-analyzer.check.command": "clippy" } \ No newline at end of file diff --git a/bindings/python/src/browsers.rs b/bindings/python/src/browsers.rs index d1c395e..13f27c9 100644 --- a/bindings/python/src/browsers.rs +++ b/bindings/python/src/browsers.rs @@ -1,6 +1,5 @@ use crate::to_dict; use pyo3::prelude::*; -use rookie::browser; use std::path::PathBuf; /// Any browser @@ -102,7 +101,7 @@ pub fn firefox_based( db_path: String, domains: Option>, ) -> PyResult> { - let cookies = browser::mozilla::firefox_based(PathBuf::from(db_path), domains)?; + let cookies = rookie::firefox_based(PathBuf::from(db_path), domains)?; let cookies = to_dict(py, cookies)?; Ok(cookies) @@ -144,8 +143,7 @@ pub fn chromium_based( db_path: String, domains: Option>, ) -> PyResult> { - let cookies = - browser::chromium::chromium_based(PathBuf::from(key_path), PathBuf::from(db_path), domains)?; + let cookies = rookie::chromium_based(PathBuf::from(key_path), PathBuf::from(db_path), domains)?; let cookies = to_dict(py, cookies)?; Ok(cookies) @@ -181,7 +179,7 @@ pub fn chromium_based( osx_key_service: None, osx_key_user: None, }; - let cookies = browser::chromium::chromium_based(&config, PathBuf::from(db_path), domains)?; + let cookies = rookie::chromium_based(&config, PathBuf::from(db_path), domains)?; let cookies = to_dict(py, cookies)?; Ok(cookies) diff --git a/bindings/python/src/lib.rs b/bindings/python/src/lib.rs index d3bb55e..524c8a5 100644 --- a/bindings/python/src/lib.rs +++ b/bindings/python/src/lib.rs @@ -1,5 +1,5 @@ use pyo3::{prelude::*, types::PyDict}; -use rookie::common::enums::Cookie; +use rookie::enums::Cookie; mod browsers; use browsers::*; diff --git a/rookie-rs/examples/simple.rs b/rookie-rs/examples/simple.rs index b4c1d7c..8e3dfe0 100644 --- a/rookie-rs/examples/simple.rs +++ b/rookie-rs/examples/simple.rs @@ -1,4 +1,4 @@ fn main() { - let cookies = rookie::chrome(None).unwrap(); + let cookies = rookie::brave(None).unwrap(); println!("{cookies:?}"); } diff --git a/rookie-rs/src/browser/chromium.rs b/rookie-rs/src/browser/chromium.rs index ce391a5..debc316 100644 --- a/rookie-rs/src/browser/chromium.rs +++ b/rookie-rs/src/browser/chromium.rs @@ -1,12 +1,11 @@ use crate::common::{date, enums::*, sqlite}; +use eyre::ContextCompat; use eyre::{bail, Result}; use std::path::PathBuf; #[cfg(target_os = "macos")] use crate::macos::secrets; -#[cfg(target_os = "windows")] -use crate::winapi; #[cfg(target_os = "windows")] use aes_gcm::{ aead::{generic_array::GenericArray, Aead, KeyInit}, @@ -16,9 +15,8 @@ use aes_gcm::{ use base64::{engine::general_purpose, Engine as _}; #[cfg(target_os = "windows")] use eyre::Context; -#[cfg(target_os = "windows")] -use serde_json; +/// Returns cookies from chromium based browser #[cfg(target_os = "windows")] pub fn chromium_based( key: PathBuf, @@ -26,7 +24,8 @@ pub fn chromium_based( domains: Option>, ) -> Result> { // Use DPAPI - let content = std::fs::read_to_string(&key)?; + + let content = std::fs::read_to_string(key)?; let key_dict: serde_json::Value = serde_json::from_str(content.as_str()).context("Can't read json file")?; @@ -42,6 +41,7 @@ pub fn chromium_based( query_cookies(keys, db_path, domains) } +/// Returns cookies from chromium based browser #[cfg(unix)] pub fn chromium_based( config: &BrowserConfig, @@ -64,11 +64,10 @@ fn create_pbkdf2_key(password: &str, salt: &[u8; 9], iterations: u32) -> Vec #[cfg(target_os = "windows")] fn get_keys(key64: &str) -> Result>> { - let mut keydpapi: Vec = general_purpose::STANDARD.decode(&key64)?; + let mut keydpapi: Vec = general_purpose::STANDARD.decode(key64)?; let keydpapi = &mut keydpapi[5..]; - let v10_key = winapi::decrypt(keydpapi)?; - let mut keys: Vec> = vec![]; - keys.push(v10_key); + let v10_key = crate::windows::dpapi::decrypt(keydpapi)?; + let keys: Vec> = vec![v10_key]; Ok(keys) } @@ -138,9 +137,9 @@ fn decrypt_encrypted_value( let ciphertext = &encrypted_value[12..]; // Create a new AES block cipher. - for key in keys { + if let Some(key) = keys.into_iter().next() { let key = Key::::from_slice(key.as_slice()); - let cipher = Aes256Gcm::new(&key); + let cipher = Aes256Gcm::new(key); let nonce = GenericArray::from_slice(nonce); // 96-bits; unique per message let plaintext = cipher .decrypt(nonce, ciphertext.as_ref()) @@ -207,13 +206,12 @@ fn query_cookies( db_path: PathBuf, domains: Option>, ) -> Result> { - /// On windows release file lock #[cfg(target_os = "windows")] { let db_path_str = db_path.to_str().context("Can't convert db path to str")?; log::warn!("Unlocking Chrome database... This may take a while (sometimes up to a minute)"); unsafe { - winapi::release_file_lock(db_path_str); + crate::windows::file_unlock::release_file_lock(db_path_str); } } diff --git a/rookie-rs/src/browser/internet_explorer.rs b/rookie-rs/src/browser/internet_explorer.rs index 2364a3e..a3a324d 100644 --- a/rookie-rs/src/browser/internet_explorer.rs +++ b/rookie-rs/src/browser/internet_explorer.rs @@ -1,18 +1,16 @@ -use crate::{ - common::{date, enums::Cookie}, - winapi, -}; +use crate::common::{date, enums::Cookie}; use eyre::Result; use libesedb::EseDb; use std::path::PathBuf; +/// Returns cookies from IE based browsers pub fn internet_explorer_based( db_path: PathBuf, domains: Option>, ) -> Result> { unsafe { if let Some(path) = db_path.to_str() { - winapi::release_file_lock(path); + crate::windows::dpapi::release_file_lock(path); } } let db = EseDb::open(db_path)?; diff --git a/rookie-rs/src/browser/mod.rs b/rookie-rs/src/browser/mod.rs index 13a0444..da0c7fd 100644 --- a/rookie-rs/src/browser/mod.rs +++ b/rookie-rs/src/browser/mod.rs @@ -1,8 +1,8 @@ -pub mod chromium; -pub mod mozilla; +pub(crate) mod chromium; +pub(crate) mod mozilla; #[cfg(target_os = "windows")] -pub mod internet_explorer; +pub(crate) mod internet_explorer; #[cfg(target_os = "macos")] -pub mod safari; +pub(crate) mod safari; diff --git a/rookie-rs/src/browser/mozilla.rs b/rookie-rs/src/browser/mozilla.rs index afeecb4..fa96ef5 100644 --- a/rookie-rs/src/browser/mozilla.rs +++ b/rookie-rs/src/browser/mozilla.rs @@ -8,6 +8,7 @@ use std::{ path::{Path, PathBuf}, }; +/// Returns cookies from mozilla based browsers pub fn firefox_based(db_path: PathBuf, domains: Option>) -> Result> { let connection = sqlite::connect(db_path.clone())?; let mut query = " diff --git a/rookie-rs/src/common/date.rs b/rookie-rs/src/common/date.rs index 70e44dd..8433a16 100644 --- a/rookie-rs/src/common/date.rs +++ b/rookie-rs/src/common/date.rs @@ -30,7 +30,7 @@ pub fn safari_timestamp(timestamp: u64) -> Option { } #[cfg(target_os = "windows")] pub fn internet_explorer_timestamp(timestamp: u64) -> Option { - if timestamp <= 0 { + if timestamp == 0 { return None; } let mut timestamp = timestamp - 116_444_736_000_000_000; diff --git a/rookie-rs/src/common/mod.rs b/rookie-rs/src/common/mod.rs index 0f256c9..9274a31 100644 --- a/rookie-rs/src/common/mod.rs +++ b/rookie-rs/src/common/mod.rs @@ -1,5 +1,5 @@ -pub mod date; +pub(crate) mod date; pub mod enums; -pub mod paths; -pub mod sqlite; -pub mod utils; +pub(crate) mod paths; +pub(crate) mod sqlite; +pub(crate) mod utils; diff --git a/rookie-rs/src/common/paths.rs b/rookie-rs/src/common/paths.rs index dfba8cd..2f00c26 100644 --- a/rookie-rs/src/common/paths.rs +++ b/rookie-rs/src/common/paths.rs @@ -98,11 +98,11 @@ pub fn find_safari_based_paths(browser_config: &BrowserConfig) -> Result Result { for path in browser_config.data_paths { // base paths - let channels: &[&str] = &browser_config.channels.as_deref().unwrap_or(&[""]); + let channels: &[&str] = browser_config.channels.unwrap_or(&[""]); for channel in channels { // channels - let path = path.replace("{channel}", &channel); + let path = path.replace("{channel}", channel); let path = expand_path(path.as_str())?; let glob_paths = expand_glob_paths(path)?; for path in glob_paths { @@ -127,7 +127,7 @@ pub fn expand_path(path: &str) -> Result { let mut expanded_path = path.to_owned(); // Iterate over all matches of the regex pattern in the input path - for capture in re.captures_iter(&path) { + for capture in re.captures_iter(path) { // Get the matched placeholder (e.g., "APPDATA" from "%APPDATA%") let placeholder = &capture[1]; diff --git a/rookie-rs/src/lib.rs b/rookie-rs/src/lib.rs index 9f22015..7d8d76e 100644 --- a/rookie-rs/src/lib.rs +++ b/rookie-rs/src/lib.rs @@ -1,39 +1,37 @@ -pub mod browser; -pub mod common; - -use browser::{chromium::chromium_based, mozilla::firefox_based}; -use common::{enums::Cookie, paths}; -use eyre::{bail, Result}; +// Public -// MacOS -#[cfg(target_os = "macos")] -pub mod macos; +// Common +pub mod common; +pub use common::enums; +// Browser +#[cfg(target_os = "windows")] +pub use browser::internet_explorer::internet_explorer_based; #[cfg(target_os = "macos")] -use browser::safari::safari_based; +pub use browser::safari::safari_based; +pub use browser::{chromium::chromium_based, mozilla::firefox_based}; +// Config +#[cfg(target_os = "linux")] +pub use linux::config; #[cfg(target_os = "macos")] pub use macos::config; - -// Windows -#[cfg(target_os = "windows")] -pub mod windows; - #[cfg(target_os = "windows")] -pub use browser::internet_explorer; +pub use windows::config; +// Private +mod browser; +use common::paths; +use enums::Cookie; +use eyre::{bail, Result}; +#[cfg(target_os = "linux")] +mod linux; #[cfg(target_os = "windows")] use std::path::PathBuf; - #[cfg(target_os = "windows")] -pub use windows::config; - -// Linux -#[cfg(target_os = "linux")] -pub mod linux; - -#[cfg(target_os = "linux")] -pub use linux::config; +mod windows; +#[cfg(target_os = "macos")] +use macos; /// Returns cookies from Firefox /// @@ -103,7 +101,7 @@ pub fn chrome(domains: Option>) -> Result> { #[cfg(target_os = "windows")] { let (key, db_path) = paths::find_chrome_based_paths(&config::CHROME_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) + chromium_based(key, db_path, domains) } #[cfg(unix)] { @@ -128,7 +126,7 @@ pub fn chromium(domains: Option>) -> Result> { #[cfg(target_os = "windows")] { let (key, db_path) = paths::find_chrome_based_paths(&config::CHROMIUM_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) + chromium_based(key, db_path, domains) } #[cfg(unix)] { @@ -153,7 +151,7 @@ pub fn brave(domains: Option>) -> Result> { #[cfg(target_os = "windows")] { let (key, db_path) = paths::find_chrome_based_paths(&config::BRAVE_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) + chromium_based(key, db_path, domains) } #[cfg(unix)] { @@ -178,7 +176,7 @@ pub fn edge(domains: Option>) -> Result> { #[cfg(target_os = "windows")] { let (key, db_path) = paths::find_chrome_based_paths(&config::EDGE_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) + chromium_based(key, db_path, domains) } #[cfg(unix)] { @@ -203,7 +201,7 @@ pub fn vivaldi(domains: Option>) -> Result> { #[cfg(target_os = "windows")] { let (key, db_path) = paths::find_chrome_based_paths(&config::VIVALDI_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) + chromium_based(key, db_path, domains) } #[cfg(unix)] { @@ -228,7 +226,7 @@ pub fn opera(domains: Option>) -> Result> { #[cfg(target_os = "windows")] { let (key, db_path) = paths::find_chrome_based_paths(&config::OPERA_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) + chromium_based(key, db_path, domains) } #[cfg(unix)] { @@ -253,7 +251,7 @@ pub fn opera_gx(domains: Option>) -> Result> { #[cfg(target_os = "windows")] { let (key, db_path) = paths::find_chrome_based_paths(&config::OPERA_GX_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) + chromium_based(key, db_path, domains) } #[cfg(unix)] { @@ -277,7 +275,7 @@ pub fn opera_gx(domains: Option>) -> Result> { #[cfg(target_os = "windows")] pub fn octo_browser(domains: Option>) -> Result> { let (key, db_path) = paths::find_chrome_based_paths(&config::OPERA_GX_CONFIG)?; - chromium_based(PathBuf::from(key), db_path, domains) + chromium_based(key, db_path, domains) } /// Returns cookies from Safari (macOS only) diff --git a/rookie-rs/src/windows/dpapi.rs b/rookie-rs/src/windows/dpapi.rs index 111dc1f..2341754 100644 --- a/rookie-rs/src/windows/dpapi.rs +++ b/rookie-rs/src/windows/dpapi.rs @@ -20,7 +20,7 @@ pub fn decrypt(keydpapi: &mut [u8]) -> Result> { // https://docs.rs/winapi/latest/winapi/um/dpapi/index.html // https://docs.rs/winapi/latest/winapi/um/winbase/fn.LocalFree.html - let mut data_in = Cryptography::CRYPT_INTEGER_BLOB { + let data_in = Cryptography::CRYPT_INTEGER_BLOB { cbData: keydpapi.len() as u32, pbData: keydpapi.as_mut_ptr(), }; @@ -31,7 +31,7 @@ pub fn decrypt(keydpapi: &mut [u8]) -> Result> { unsafe { let _ = match Cryptography::CryptUnprotectData( - &mut data_in, + &data_in, Some(ptr::null_mut()), Some(ptr::null_mut()), Some(ptr::null_mut()), @@ -101,5 +101,5 @@ pub unsafe fn release_file_lock(file_path: &str) -> bool { RmEndSession(session); return false; } - return false; + false } diff --git a/rookie-rs/src/windows/file_unlock.rs b/rookie-rs/src/windows/file_unlock.rs index 7d4b433..2e6ff89 100644 --- a/rookie-rs/src/windows/file_unlock.rs +++ b/rookie-rs/src/windows/file_unlock.rs @@ -1,12 +1,7 @@ -use std::{ffi::c_void, ptr}; - -use eyre::{anyhow, bail, Result}; use windows::{ core::{HSTRING, PCWSTR, PWSTR}, Win32::{ - Foundation, Foundation::{ERROR_MORE_DATA, ERROR_SUCCESS, WIN32_ERROR}, - Security::Cryptography, System::RestartManager::{ RmEndSession, RmForceShutdown, RmGetList, RmRegisterResources, RmShutdown, RmStartSession, CCH_RM_SESSION_KEY, RM_PROCESS_INFO, @@ -56,5 +51,5 @@ pub unsafe fn release_file_lock(file_path: &str) -> bool { RmEndSession(session); return false; } - return false; + false } diff --git a/rookie-rs/src/windows/mod.rs b/rookie-rs/src/windows/mod.rs index da98f52..081543b 100644 --- a/rookie-rs/src/windows/mod.rs +++ b/rookie-rs/src/windows/mod.rs @@ -1,2 +1,3 @@ -mod config; -mod file_unlock; +pub mod config; +pub(crate) mod dpapi; +pub(crate) mod file_unlock; From 8a1af8ce5173e7ecba46b469a1024850203ba825 Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Mon, 22 Apr 2024 00:23:16 +0300 Subject: [PATCH 08/10] fix macos clippy --- rookie-rs/src/browser/chromium.rs | 3 +-- rookie-rs/src/lib.rs | 4 ++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/rookie-rs/src/browser/chromium.rs b/rookie-rs/src/browser/chromium.rs index debc316..48acd6f 100644 --- a/rookie-rs/src/browser/chromium.rs +++ b/rookie-rs/src/browser/chromium.rs @@ -98,9 +98,8 @@ fn get_keys(config: &BrowserConfig) -> Result>> { #[cfg(target_os = "macos")] fn get_keys(config: &BrowserConfig) -> Result>> { let salt = b"saltysalt"; - let iterations: u32; - iterations = 1003; + let iterations = 1003; let mut keys: Vec> = vec![]; diff --git a/rookie-rs/src/lib.rs b/rookie-rs/src/lib.rs index 7d8d76e..236d039 100644 --- a/rookie-rs/src/lib.rs +++ b/rookie-rs/src/lib.rs @@ -28,10 +28,10 @@ use eyre::{bail, Result}; mod linux; #[cfg(target_os = "windows")] use std::path::PathBuf; +#[cfg(target_os = "macos")] +mod macos; #[cfg(target_os = "windows")] mod windows; -#[cfg(target_os = "macos")] -use macos; /// Returns cookies from Firefox /// From 560aede3c3e1369faa1b98cfa4970cc42de33d55 Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Mon, 22 Apr 2024 00:31:30 +0300 Subject: [PATCH 09/10] fix clippy error --- rookie-rs/src/browser/chromium.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/rookie-rs/src/browser/chromium.rs b/rookie-rs/src/browser/chromium.rs index 48acd6f..e31c1cd 100644 --- a/rookie-rs/src/browser/chromium.rs +++ b/rookie-rs/src/browser/chromium.rs @@ -1,8 +1,10 @@ use crate::common::{date, enums::*, sqlite}; -use eyre::ContextCompat; use eyre::{bail, Result}; use std::path::PathBuf; +#[cfg(not(target_os = "linux"))] +use eyre::ContextCompat; + #[cfg(target_os = "macos")] use crate::macos::secrets; From 2f61328224fc659e4de9d4b0a8ef1c94f267a044 Mon Sep 17 00:00:00 2001 From: thewh1teagle <61390950+thewh1teagle@users.noreply.github.com> Date: Mon, 22 Apr 2024 00:44:25 +0300 Subject: [PATCH 10/10] update docs --- docs/General.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/General.md b/docs/General.md index 63bc143..9224d04 100644 --- a/docs/General.md +++ b/docs/General.md @@ -2,12 +2,16 @@ ## Password prompt -This library may trigger a password prompt with kde-wallet on linux / macOS with chromium based browsers when accessing browser cookies. +This library may trigger a password prompt with kde-wallet on linux / macOS with chromium based browsers when accessing browser cookies. ## Session Cookies Retrieval Chrome-based browsers have a security feature that prevents external access to their cookies file. To bypass this security measure, we restart the browser seamlessly. As a result, session cookies are retrieved and can be used, but they will expire once the browser is closed again. +## MacOS Safari permission denied + +On recent versions of macOS the path of the cookies at `/Users/user/Library/Containers/com.apple.Safari/Data/Library/Cookies/Cookies.binarycookies` has restricted access. +You can grant full disk access through `System Settings` -> Search for `Full disk access` -> And enable it for terminal / the app you running from. ## Using on Unsupported platforms @@ -28,7 +32,7 @@ And pull the Cookies file you want and then execute `CLI` on that file To import cookies from rookiepy into the browser, -you can execute short javascript code in the browser console +you can execute short javascript code in the browser console and construct the cookies manually, @@ -58,6 +62,6 @@ print(create_js_code(cookies)) In this example, I extracted the cookies from the `Brave` browser from the domain 'github.com.' -I cleared all of my browser cookies, executed the code, copied the output. +I cleared all of my browser cookies, executed the code, copied the output. -Then, I opened `github.com` in the browser and pasted it into the console. As a result, I was logged in into my account. \ No newline at end of file +Then, I opened `github.com` in the browser and pasted it into the console. As a result, I was logged in into my account.