Skip to content

Commit

Permalink
Use low-level package for keyring mutex
Browse files Browse the repository at this point in the history
Signed-off-by: Timothy Johnson <[email protected]>
  • Loading branch information
t1m0thyj committed Dec 23, 2024
1 parent 9475e16 commit 7c0a0d7
Show file tree
Hide file tree
Showing 5 changed files with 55 additions and 351 deletions.
181 changes: 18 additions & 163 deletions packages/secrets/core/Cargo.lock

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

4 changes: 2 additions & 2 deletions packages/secrets/core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,11 @@ version = "0.48.0"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.9.3"
core-foundation-sys = "0.8.4"
named-lock = "0.4.1"
fmutex = "0.1.0"

[target.'cfg(any(target_os = "freebsd", target_os = "linux"))'.dependencies]
gio = "0.18.2"
glib = "0.18.2"
glib-sys = "0.18.1"
gio = "0.18.2"
libsecret = "0.4.0"
libsecret-sys = "0.4.0"
1 change: 0 additions & 1 deletion packages/secrets/core/src/os/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ pub enum KeyringError {
#[error("[keyring] {name:?} library returned an error:\n\n{details:?}")]
Library { name: String, details: String },

#[cfg(not(target_os = "macos"))]
#[error("[keyring] An OS error has occurred:\n\n{0}")]
Os(String),

Expand Down
29 changes: 15 additions & 14 deletions packages/secrets/core/src/os/mac/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ use error::Error;

use crate::os::mac::error::ERR_SEC_ITEM_NOT_FOUND;
use crate::os::mac::keychain_search::{KeychainSearch, SearchResult};
use fmutex::Guard;
use keychain::SecKeychain;
use named_lock::NamedLock;

impl From<Error> for KeyringError {
fn from(error: Error) -> Self {
Expand All @@ -23,24 +23,25 @@ impl From<Error> for KeyringError {
}
}

impl From<named_lock::Error> for KeyringError {
fn from(error: named_lock::Error) -> Self {
KeyringError::Library {
name: "named-lock".to_owned(),
details: error.to_string(),
}
impl From<std::io::Error> for KeyringError {
fn from(error: std::io::Error) -> Self {
KeyringError::Os(error.to_string())
}
}

fn keyring_mutex() -> Result<NamedLock, KeyringError> {
fn keyring_mutex() -> Result<Guard, KeyringError> {
// MacOS shows keychain prompt after secret has been modified by another process. We use cross-process mutex to
// block keychain access if there are multiple concurrent keychain operations invoked by the same process. This
// prevents multiple instances of the same app (e.g. VS Code) from triggering several keychain prompts at once.
let exe_path = std::env::current_exe()
.unwrap()
.to_string_lossy()
.replace(std::path::MAIN_SEPARATOR, "_");
NamedLock::create(format!("keyring_{}", exe_path).as_str()).map_err(|e| KeyringError::from(e))
let lock_path = std::env::temp_dir()
.join(format!("keyring_{}.lock", exe_path));
std::fs::OpenOptions::new().create(true).write(true).open(&lock_path)
.and_then(|_| fmutex::lock(lock_path))
.map_err(KeyringError::from)
}

///
Expand All @@ -59,7 +60,7 @@ pub fn set_password(
password: &String,
) -> Result<bool, KeyringError> {
let keychain = SecKeychain::default().unwrap();
let _lock = keyring_mutex()?.lock().unwrap();
let _lock = keyring_mutex().unwrap();
match keychain.set_password(service.as_str(), account.as_str(), password.as_bytes()) {
Ok(()) => Ok(true),
Err(err) => Err(KeyringError::from(err)),
Expand All @@ -78,7 +79,7 @@ pub fn set_password(
///
pub fn get_password(service: &String, account: &String) -> Result<Option<String>, KeyringError> {
let keychain = SecKeychain::default().unwrap();
let _lock = keyring_mutex()?.lock().unwrap();
let _lock = keyring_mutex().unwrap();
match keychain.find_password(service.as_str(), account.as_str()) {
Ok((pw, _)) => Ok(Some(String::from_utf8(pw.to_owned())?)),
Err(err) if err.code() == ERR_SEC_ITEM_NOT_FOUND => Ok(None),
Expand Down Expand Up @@ -106,7 +107,7 @@ pub fn find_password(service: &String) -> Result<Option<String>, KeyringError> {
}

let keychain = SecKeychain::default().unwrap();
let _lock = keyring_mutex()?.lock().unwrap();
let _lock = keyring_mutex().unwrap();
match keychain.find_password(cred_attrs[0], cred_attrs[1]) {
Ok((pw, _)) => {
let pw_str = String::from_utf8(pw.to_owned())?;
Expand All @@ -128,7 +129,7 @@ pub fn find_password(service: &String) -> Result<Option<String>, KeyringError> {
///
pub fn delete_password(service: &String, account: &String) -> Result<bool, KeyringError> {
let keychain = SecKeychain::default().unwrap();
let _lock = keyring_mutex()?.lock().unwrap();
let _lock = keyring_mutex().unwrap();
match keychain.find_password(service.as_str(), account.as_str()) {
Ok((_, item)) => {
item.delete()?;
Expand All @@ -153,7 +154,7 @@ pub fn find_credentials(
service: &String,
credentials: &mut Vec<(String, String)>,
) -> Result<bool, KeyringError> {
let _lock = keyring_mutex()?.lock().unwrap();
let _lock = keyring_mutex().unwrap();
match KeychainSearch::new()
.label(service.as_str())
.with_attrs()
Expand Down
Loading

0 comments on commit 7c0a0d7

Please sign in to comment.