Skip to content

Commit 74ee254

Browse files
committed
WIP: portal impl
1 parent 9414a57 commit 74ee254

File tree

6 files changed

+273
-2
lines changed

6 files changed

+273
-2
lines changed

.github/workflows/CI.yml

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,11 @@ jobs:
2525
with:
2626
working-directory: ./keyring-util
2727
command: check
28+
- name: Check (oo7-portal-impl)
29+
uses: actions-rs/cargo@v1
30+
with:
31+
command: check
32+
working-directory: ./oo7-portal-impl
2833

2934
test:
3035
name: Test Suite
@@ -48,8 +53,10 @@ jobs:
4853
dbus-run-session -- cargo test --no-default-features --features async-std --features openssl_crypto
4954
- name: Build (keyring-util)
5055
working-directory: ./keyring-util
51-
run: |
52-
cargo build
56+
run: cargo build
57+
- name: Build and test (oo7-portal-impl)
58+
working-directory: ./oo7-portal-impl
59+
run: cargo build
5360

5461
fmt:
5562
name: Rustfmt
@@ -73,6 +80,12 @@ jobs:
7380
command: fmt
7481
working-directory: ./keyring-util
7582
args: --all -- --check
83+
- name: Rust Format (oo7-portal-impl)
84+
uses: actions-rs/cargo@v1
85+
with:
86+
command: fmt
87+
working-directory: ./oo7-portal-impl
88+
args: --all -- --check
7689

7790
clippy:
7891
name: Clippy
@@ -96,3 +109,9 @@ jobs:
96109
command: clippy
97110
working-directory: ./keyring-util
98111
args: -- -D warnings
112+
- name: Clippy (oo7-portal-impl)
113+
uses: actions-rs/cargo@v1
114+
with:
115+
command: clippy
116+
working-directory: ./oo7-portal-impl
117+
args: -- -D warnings

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
2+
/oo7-portal-impl/target
23
Cargo.lock
34
keyring-util/target

oo7-portal-impl/Cargo.toml

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[package]
2+
name = "oo7-portal-impl"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
async-std = { version = "1.11", features = [ "attributes" ] }
8+
futures = "0.3"
9+
futures-channel = "0.3"
10+
oo7 = { path = "../" }
11+
ring = "0.17.5"
12+
secrecy = { version = "0.8", features = [ "alloc" ] }
13+
serde = { version = "1.0", features = ["derive"] }
14+
tracing = "0.1"
15+
tracing-subscriber = "0.3"
16+
zbus = "3.5.0"
17+
zeroize = { version = "1", features = ["zeroize_derive"] }
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
[portal]
2+
DBusName=org.freedesktop.impl.portal.desktop.oo7
3+
Interfaces=org.freedesktop.impl.portal.Secret
4+
UseIn=gnome
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[D-BUS Service]
2+
Name=org.freedesktop.impl.portal.Secret
3+
Exec=@bindir@/oo7-portal-impl

oo7-portal-impl/src/main.rs

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
use std::{
2+
borrow::BorrowMut,
3+
collections::HashMap,
4+
future::pending,
5+
os::fd::{AsRawFd, FromRawFd},
6+
sync::{Arc, Mutex},
7+
};
8+
9+
use async_std::io::WriteExt;
10+
use futures::FutureExt;
11+
use oo7::{
12+
dbus::{Algorithm, Service},
13+
zbus::{self, dbus_interface, zvariant, zvariant::Type},
14+
};
15+
use ring::rand::SecureRandom;
16+
use serde::Serialize;
17+
use zvariant::OwnedObjectPath;
18+
19+
const PORTAL_SECRET_SIZE: usize = 64;
20+
const NAME: &str = "org.freedesktop.impl.portal.desktop.oo7";
21+
const PATH: &str = "/org/freedesktop/portal/desktop";
22+
23+
#[derive(Serialize, PartialEq, Eq, Debug, Type)]
24+
#[doc(hidden)]
25+
enum ResponseType {
26+
Success = 0,
27+
Cancelled = 1,
28+
Other = 2,
29+
}
30+
31+
#[derive(zbus::DBusError, Debug)]
32+
enum Error {
33+
Msg(String),
34+
}
35+
36+
impl Error {
37+
fn new(msg: &str) -> Error {
38+
Error::Msg(msg.to_string())
39+
}
40+
}
41+
42+
impl From<oo7::dbus::Error> for Error {
43+
fn from(err: oo7::dbus::Error) -> Self {
44+
Error::new(&err.to_string())
45+
}
46+
}
47+
48+
struct Secret;
49+
50+
#[dbus_interface(name = "org.freedesktop.impl.portal.Secret")]
51+
impl Secret {
52+
#[dbus_interface(property, name = "version")]
53+
fn version(&self) -> u32 {
54+
0
55+
}
56+
57+
#[dbus_interface(out_args("response", "results"))]
58+
async fn retrieve_secret(
59+
&self,
60+
#[zbus(object_server)] object_server: &zbus::ObjectServer,
61+
handle: OwnedObjectPath,
62+
app_id: &str,
63+
fd: zvariant::Fd,
64+
_options: HashMap<&str, zvariant::Value<'_>>,
65+
) -> Result<(ResponseType, HashMap<&str, zvariant::OwnedValue>), Error> {
66+
tracing::debug!("Got request from {app_id} with options: {_options:?}");
67+
68+
let (sender, receiver) = futures_channel::oneshot::channel();
69+
70+
if let Err(err) = Request::serve(object_server, handle.clone(), sender).await {
71+
tracing::error!("Could not register object {handle}: {err}");
72+
}
73+
74+
let fut_1 = async move {
75+
let res = match retrieve_secret_inner(app_id, fd).await {
76+
Ok(res) => Ok((ResponseType::Success, res)),
77+
Err(err) => {
78+
tracing::error!("could not retrieve secret: {err}");
79+
Ok((ResponseType::Other, HashMap::new()))
80+
}
81+
};
82+
83+
// We do not accept Close request anymore here.
84+
tracing::debug!("Object {handle} handled");
85+
object_server
86+
.remove::<Request, &zvariant::OwnedObjectPath>(&handle)
87+
.await
88+
.unwrap();
89+
90+
res
91+
};
92+
93+
let fut_2 = async move {
94+
receiver.await.unwrap();
95+
Ok((ResponseType::Cancelled, HashMap::new()))
96+
};
97+
98+
let t1 = fut_1.fuse();
99+
let t2 = fut_2.fuse();
100+
101+
futures::pin_mut!(t1, t2);
102+
103+
futures::select! {
104+
fut_1_res = t1 => fut_1_res,
105+
fut_2_res = t2 => fut_2_res,
106+
}
107+
}
108+
}
109+
110+
fn generate_secret() -> Result<zeroize::Zeroizing<Vec<u8>>, Error> {
111+
let mut secret = [0; PORTAL_SECRET_SIZE];
112+
let rand = ring::rand::SystemRandom::new();
113+
rand.fill(secret.borrow_mut())
114+
.map_err(|err| Error::new(&err.to_string()))?;
115+
Ok(zeroize::Zeroizing::new(secret.to_vec()))
116+
}
117+
118+
async fn retrieve_secret_inner(
119+
app_id: &str,
120+
fd: zvariant::Fd,
121+
) -> Result<HashMap<&'static str, zvariant::OwnedValue>, Error> {
122+
let service = Service::new(Algorithm::Encrypted)
123+
.await
124+
.or(Service::new(Algorithm::Plain).await)?;
125+
let collection = service.default_collection().await?;
126+
let attributes = HashMap::from([("app_id", app_id)]);
127+
128+
let secret = if let Some(item) = collection
129+
.search_items(attributes.clone())
130+
.await
131+
.map_err(|err| Error::new(&err.to_string()))?
132+
.first()
133+
{
134+
item.secret().await?
135+
} else {
136+
tracing::debug!("Could not find secret for {app_id}, creating one");
137+
let secret = generate_secret()?;
138+
collection
139+
.create_item(
140+
&format!("Secret Portal token for {app_id}"),
141+
attributes,
142+
&secret,
143+
true,
144+
// TODO Find a better one.
145+
"text/plain",
146+
)
147+
.await?;
148+
149+
secret
150+
};
151+
152+
// Write the secret to the FD.
153+
let raw_fd = fd.as_raw_fd();
154+
let mut stream = unsafe { async_std::os::unix::net::UnixStream::from_raw_fd(raw_fd) };
155+
stream
156+
.write_all(&secret)
157+
.await
158+
.map_err(|e| Error::new(&e.to_string()))?;
159+
160+
Ok(HashMap::new())
161+
}
162+
163+
#[async_std::main]
164+
async fn main() -> Result<(), zbus::Error> {
165+
tracing_subscriber::fmt::init();
166+
167+
let backend = Secret;
168+
let cnx = zbus::ConnectionBuilder::session()?
169+
// .name(NAME)?
170+
.serve_at(PATH, backend)?
171+
.build()
172+
.await?;
173+
// NOTE For debugging.
174+
let flags = zbus::fdo::RequestNameFlags::ReplaceExisting
175+
| zbus::fdo::RequestNameFlags::AllowReplacement;
176+
cnx.request_name_with_flags(NAME, flags).await?;
177+
178+
loop {
179+
pending::<()>().await;
180+
}
181+
}
182+
183+
struct Request {
184+
handle_path: zvariant::OwnedObjectPath,
185+
sender: Arc<Mutex<Option<futures_channel::oneshot::Sender<()>>>>,
186+
}
187+
188+
#[dbus_interface(name = "org.freedesktop.impl.portal.Request")]
189+
impl Request {
190+
async fn close(
191+
&self,
192+
#[zbus(object_server)] server: &zbus::ObjectServer,
193+
) -> zbus::fdo::Result<()> {
194+
tracing::debug!("Object {} closed", self.handle_path);
195+
server
196+
.remove::<Self, &zvariant::OwnedObjectPath>(&self.handle_path)
197+
.await?;
198+
199+
if let Ok(mut guard) = self.sender.lock() {
200+
if let Some(sender) = (*guard).take() {
201+
// This will Err out if the receiver has been dropped.
202+
let _ = sender.send(());
203+
}
204+
}
205+
206+
Ok(())
207+
}
208+
}
209+
210+
impl Request {
211+
async fn serve(
212+
object_server: &zbus::ObjectServer,
213+
handle_path: OwnedObjectPath,
214+
sender: futures_channel::oneshot::Sender<()>,
215+
) -> zbus::fdo::Result<()> {
216+
tracing::debug!("Handling object {:?}", handle_path.as_str());
217+
218+
let iface = Request {
219+
handle_path: handle_path.clone(),
220+
sender: Arc::new(Mutex::new(Some(sender))),
221+
};
222+
223+
object_server.at(handle_path, iface).await?;
224+
225+
Ok(())
226+
}
227+
}

0 commit comments

Comments
 (0)