secret_service/
util.rs

1//! Contains helpers for:
2//!   locking/unlocking
3//!   exec_prompt
4//!   formatting secrets
5
6use crate::error::Error;
7use crate::proxy::prompt::{Completed, PromptProxy, PromptProxyBlocking};
8use crate::proxy::service::{ServiceProxy, ServiceProxyBlocking};
9use crate::proxy::SecretStruct;
10use crate::session::encrypt;
11use crate::session::Session;
12use crate::ss::SS_DBUS_NAME;
13
14use futures_util::StreamExt;
15use zbus::{
16    proxy::CacheProperties,
17    zvariant::{self, ObjectPath},
18};
19
20// Helper enum for locking
21pub(crate) enum LockAction {
22    Lock,
23    Unlock,
24}
25
26pub(crate) async fn lock_or_unlock(
27    conn: zbus::Connection,
28    service_proxy: &ServiceProxy<'_>,
29    object_path: &ObjectPath<'_>,
30    lock_action: LockAction,
31) -> Result<(), Error> {
32    let objects = vec![object_path];
33
34    let lock_action_res = match lock_action {
35        LockAction::Lock => service_proxy.lock(objects).await?,
36        LockAction::Unlock => service_proxy.unlock(objects).await?,
37    };
38
39    if lock_action_res.object_paths.is_empty() {
40        exec_prompt(conn, &lock_action_res.prompt).await?;
41    }
42    Ok(())
43}
44
45pub(crate) fn lock_or_unlock_blocking(
46    conn: zbus::blocking::Connection,
47    service_proxy: &ServiceProxyBlocking,
48    object_path: &ObjectPath,
49    lock_action: LockAction,
50) -> Result<(), Error> {
51    let objects = vec![object_path];
52
53    let lock_action_res = match lock_action {
54        LockAction::Lock => service_proxy.lock(objects)?,
55        LockAction::Unlock => service_proxy.unlock(objects)?,
56    };
57
58    if lock_action_res.object_paths.is_empty() {
59        exec_prompt_blocking(conn, &lock_action_res.prompt)?;
60    }
61    Ok(())
62}
63
64pub(crate) fn format_secret(
65    session: &Session,
66    secret: &[u8],
67    content_type: &str,
68) -> Result<SecretStruct, Error> {
69    let content_type = content_type.to_owned();
70
71    if let Some(session_key) = session.get_aes_key() {
72        let mut aes_iv = [0; 16];
73        getrandom::getrandom(&mut aes_iv).expect("platform RNG failed");
74
75        let encrypted_secret = encrypt(secret, session_key, &aes_iv);
76
77        // Construct secret struct
78        let parameters = aes_iv.to_vec();
79        let value = encrypted_secret;
80
81        Ok(SecretStruct {
82            session: session.object_path.clone(),
83            parameters,
84            value,
85            content_type,
86        })
87    } else {
88        // just Plain for now
89        let parameters = Vec::new();
90        let value = secret.to_vec();
91
92        Ok(SecretStruct {
93            session: session.object_path.clone(),
94            parameters,
95            value,
96            content_type,
97        })
98    }
99}
100
101// TODO: Users could pass their own window ID in.
102const NO_WINDOW_ID: &str = "";
103
104pub(crate) async fn exec_prompt(
105    conn: zbus::Connection,
106    prompt: &ObjectPath<'_>,
107) -> Result<zvariant::OwnedValue, Error> {
108    let prompt_proxy = PromptProxy::builder(&conn)
109        .destination(SS_DBUS_NAME)?
110        .path(prompt)?
111        .cache_properties(CacheProperties::No)
112        .build()
113        .await?;
114
115    let mut receive_completed_iter = prompt_proxy.receive_completed().await?;
116    prompt_proxy.prompt(NO_WINDOW_ID).await?;
117
118    handle_signal(receive_completed_iter.next().await.unwrap())
119}
120
121pub(crate) fn exec_prompt_blocking(
122    conn: zbus::blocking::Connection,
123    prompt: &ObjectPath,
124) -> Result<zvariant::OwnedValue, Error> {
125    let prompt_proxy = PromptProxyBlocking::builder(&conn)
126        .destination(SS_DBUS_NAME)?
127        .path(prompt)?
128        .cache_properties(CacheProperties::No)
129        .build()?;
130
131    let mut receive_completed_iter = prompt_proxy.receive_completed()?;
132    prompt_proxy.prompt(NO_WINDOW_ID)?;
133
134    handle_signal(receive_completed_iter.next().unwrap())
135}
136
137fn handle_signal(signal: Completed) -> Result<zvariant::OwnedValue, Error> {
138    let args = signal.args()?;
139    if args.dismissed {
140        Err(Error::Prompt)
141    } else {
142        zvariant::OwnedValue::try_from(args.result).map_err(From::from)
143    }
144}
145
146pub(crate) fn handle_conn_error(e: zbus::Error) -> Error {
147    match e {
148        zbus::Error::InterfaceNotFound | zbus::Error::Address(_) => Error::Unavailable,
149        zbus::Error::InputOutput(e) if e.kind() == std::io::ErrorKind::NotFound => {
150            Error::Unavailable
151        }
152        e => e.into(),
153    }
154}