Skip to content

Commit

Permalink
Add prompt implementation for service method unlock
Browse files Browse the repository at this point in the history
Signed-off-by: Dhanuka Warusadura <[email protected]>
  • Loading branch information
warusadura committed Jan 19, 2025
1 parent 69d9374 commit 23067f3
Show file tree
Hide file tree
Showing 2 changed files with 138 additions and 2 deletions.
118 changes: 117 additions & 1 deletion server/src/gnome/prompter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,24 @@ impl Properties {
cancel_label: Some("Cancel".to_string()),
}
}

fn for_unlock() -> Self {
Self {
title: Some("Unlock Keyring".to_string()),
choice_label: None,
description: Some(
"An application wants access to the keyring 'login', but it is locked".to_string(),
),
message: Some("Authentication required".to_string()),
caller_window: None,
warning: None,
password_new: Some(false),
password_strength: Some(0),
choice_chosen: Some(false),
continue_label: Some("Unlock".to_string()),
cancel_label: Some("Cancel".to_string()),
}
}
}

#[derive(Debug, Type)]
Expand Down Expand Up @@ -219,7 +237,105 @@ impl PrompterCallback {
}
}
}
PromptRole::Unlock => todo!(),
PromptRole::Unlock => {
match reply {
Reply::Empty => {
// First PromptReady call
let secret_exchange = SecretExchange::new().map_err(|err| {
ServiceError::ZBus(zbus::Error::FDO(Box::new(
zbus::fdo::Error::Failed(format!(
"Failed to generate SecretExchange {err}."
)),
)))
})?;
let daemon_exchange = secret_exchange.begin();
let aes_key =
secret_exchange
.create_shared_secret(exchange)
.map_err(|err| {
ServiceError::ZBus(zbus::Error::FDO(Box::new(
zbus::fdo::Error::Failed(format!(
"Failed to generate AES key for SecretExchange {err}."
)),
)))
})?;
self.service.set_secret_exchange_key(aes_key).await;

let properties = Properties::for_unlock();
let path = self.path.clone();

tokio::spawn(PrompterCallback::perform_prompt(
connection.clone(),
path,
PromptType::Confirm,
properties,
daemon_exchange,
));
}
Reply::No => {
// Second PromptReady call and the prompt is dismissed
tracing::debug!("Prompt is being dismissed.");

tokio::spawn(PrompterCallback::stop_prompting(
connection.clone(),
self.path.clone(),
));

let signal_emitter = self.service.signal_emitter(prompt.path().clone())?;
let result = Value::new::<Vec<OwnedObjectPath>>(vec![])
.try_to_owned()
.unwrap();

tokio::spawn(PrompterCallback::prompt_completed(
signal_emitter,
true,
result,
));
}
Reply::Yes => {
// Second PromptReady call with the final exchange.
// Verify secret
if let Some(secret) = SecretExchange::retrieve_secret(
exchange,
&self.service.secret_exchange_key().await,
) {
match oo7::file::Keyring::open("login", secret).await {
Ok(_) => {
tracing::debug!("Login keyring secret matches.");
}
Err(oo7::file::Error::IncorrectSecret) => {
tracing::error!("Login keyring incorrect secret.");
// TODO: offer the prompt again to re-enter
// the password.
// TODO: limit number attempts.
}
Err(_) => todo!(),
}
}

let service = self.service.clone();
let objects = prompt.objects().clone();
let result = Value::new(&objects).try_to_owned().unwrap();

tokio::spawn(async move {
let _ = service.set_locked(true, &objects, false).await;
});

tokio::spawn(PrompterCallback::stop_prompting(
connection.clone(),
self.path.clone(),
));

let signal_emitter = self.service.signal_emitter(prompt.path().clone())?;

tokio::spawn(PrompterCallback::prompt_completed(
signal_emitter,
false,
result,
));
}
}
}
PromptRole::CreateCollection => todo!(),
};

Expand Down
22 changes: 21 additions & 1 deletion server/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ pub struct Service {
session_index: Arc<RwLock<u32>>,
prompts: Arc<Mutex<Vec<Prompt>>>,
prompt_index: Arc<RwLock<u32>>,
// SecretExchange aes_key
secret_exchange_key: Arc<RwLock<String>>,
}

#[zbus::interface(name = "org.freedesktop.Secret.Service")]
Expand Down Expand Up @@ -137,8 +139,17 @@ impl Service {
pub async fn unlock(
&self,
objects: Vec<OwnedObjectPath>,
#[zbus(object_server)] object_server: &zbus::ObjectServer,
) -> Result<(Vec<OwnedObjectPath>, OwnedObjectPath), ServiceError> {
let (unlocked, _not_unlocked) = self.set_locked(false, &objects, false).await?;
let (unlocked, not_unlocked) = self.set_locked(false, &objects, false).await?;
if !not_unlocked.is_empty() {
let prompt = Prompt::new(self.clone(), not_unlocked, PromptRole::Unlock).await;
self.prompts.lock().await.push(prompt.clone());
let path = prompt.path().clone();

object_server.at(&path, prompt).await?;
return Ok((unlocked, path));
}

Ok((unlocked, OwnedObjectPath::default()))
}
Expand Down Expand Up @@ -291,6 +302,7 @@ impl Service {
session_index: Default::default(),
prompts: Default::default(),
prompt_index: Default::default(),
secret_exchange_key: Default::default(),
};

object_server
Expand Down Expand Up @@ -452,4 +464,12 @@ impl Service {
// it should be cleaned up afterwards.
self.prompts.lock().await.pop();
}

pub async fn set_secret_exchange_key(&self, aes_key: String) {
*self.secret_exchange_key.write().await = aes_key;
}

pub async fn secret_exchange_key(&self) -> String {
self.secret_exchange_key.read().await.clone()
}
}

0 comments on commit 23067f3

Please sign in to comment.