Skip to main content

dbus_router/
dbus_daemon.rs

1//! Special handling for org.freedesktop.DBus daemon methods
2//!
3//! This module handles methods that require special processing:
4//! - ListNames: merge results from both buses
5//! - GetNameOwner: rewrite unique name in response
6//! - NameOwnerChanged: rewrite unique names in signal body
7
8use crate::fake_name::{from_fake_name, is_unique_name, to_fake_name};
9use crate::message::{Endian, Message};
10use crate::message_rewrite::{parse_match_rule_sender, rewrite_match_rule_sender};
11use crate::session::Bus;
12use anyhow::{bail, Result};
13use zvariant::{
14    serialized::{Context, Data},
15    to_bytes, Endian as ZEndian, BE, LE,
16};
17
18/// Methods that need response rewriting (unique name in return value)
19pub const METHODS_NEED_RESPONSE_REWRITE: &[&str] = &["Hello", "GetNameOwner", "ListQueuedOwners"];
20
21/// Methods that need request rewriting (unique name in argument)
22pub const METHODS_NEED_REQUEST_REWRITE: &[&str] = &[
23    "GetConnectionCredentials",
24    "GetConnectionUnixUser",
25    "GetConnectionUnixProcessID",
26    "GetConnectionSELinuxSecurityContext",
27    "GetAdtAuditSessionData",
28    "NameHasOwner",
29    "GetNameOwner",
30];
31
32/// Methods that need result merging from both buses
33pub const METHODS_NEED_MERGE: &[&str] = &["ListNames", "ListActivatableNames"];
34
35/// Signals that need body rewriting
36pub const SIGNALS_NEED_REWRITE: &[&str] = &["NameOwnerChanged"];
37
38/// Check if a method needs response rewriting
39pub fn needs_response_rewrite(member: &str) -> bool {
40    METHODS_NEED_RESPONSE_REWRITE.contains(&member)
41}
42
43/// Check if a method needs request rewriting
44pub fn needs_request_rewrite(member: &str) -> bool {
45    METHODS_NEED_REQUEST_REWRITE.contains(&member)
46}
47
48/// Check if a method needs result merging
49pub fn needs_merge(member: &str) -> bool {
50    METHODS_NEED_MERGE.contains(&member)
51}
52
53/// Check if a signal needs body rewriting
54pub fn signal_needs_rewrite(member: &str) -> bool {
55    SIGNALS_NEED_REWRITE.contains(&member)
56}
57
58/// Rewrite AddMatch/RemoveMatch body to remove fake prefix from sender.
59/// Returns the rewritten message bytes if sender was rewritten, or None if no rewrite needed.
60pub fn rewrite_match_rule_body(msg: &Message) -> Result<Option<Vec<u8>>> {
61    let body_start = msg.body_start();
62
63    if body_start >= msg.raw.len() {
64        return Ok(None);
65    }
66
67    // Parse the string from body
68    let str_len = msg.header.endian.read_u32(&msg.raw[body_start..]) as usize;
69    let str_start = body_start + 4;
70    let str_end = str_start + str_len;
71
72    if str_end > msg.raw.len() {
73        return Ok(None);
74    }
75
76    let rule = String::from_utf8_lossy(&msg.raw[str_start..str_end]).to_string();
77
78    // Check if sender in match rule is a fake unique name
79    if let Some(sender) = parse_match_rule_sender(&rule) {
80        if let Some((real_sender, _bus)) = from_fake_name(&sender) {
81            // Rewrite the match rule with the real sender
82            let new_rule = rewrite_match_rule_sender(&rule, &sender, &real_sender);
83            tracing::trace!(
84                old_sender = %sender,
85                new_sender = %real_sender,
86                "Rewrote match rule sender"
87            );
88            return rebuild_message_with_string(&msg.raw, msg.header.endian, body_start, &new_rule)
89                .map(Some);
90        }
91    }
92
93    Ok(None)
94}
95
96/// Rewrite a single unique name in response body (for GetNameOwner, Hello)
97pub fn rewrite_single_name_response(msg: &Message, source: Bus) -> Result<Vec<u8>> {
98    let body_start = msg.body_start();
99
100    if body_start >= msg.raw.len() {
101        return Ok(msg.raw.clone());
102    }
103
104    // Parse the string from body
105    let str_len = msg.header.endian.read_u32(&msg.raw[body_start..]) as usize;
106    let str_start = body_start + 4;
107    let str_end = str_start + str_len;
108
109    if str_end > msg.raw.len() {
110        return Ok(msg.raw.clone());
111    }
112
113    let name = String::from_utf8_lossy(&msg.raw[str_start..str_end]).to_string();
114
115    // Only rewrite if it's a unique name
116    if !is_unique_name(&name) {
117        return Ok(msg.raw.clone());
118    }
119
120    let fake_name = to_fake_name(&name, source);
121
122    // Build new message with rewritten name
123    rebuild_message_with_string(&msg.raw, msg.header.endian, body_start, &fake_name)
124}
125
126/// Rewrite unique names in string array response (for ListQueuedOwners)
127pub fn rewrite_string_array_response(msg: &Message, source: Bus) -> Result<Vec<u8>> {
128    let names = parse_string_array(msg)?;
129
130    if names.is_empty() {
131        return Ok(msg.raw.clone());
132    }
133
134    // Rewrite each unique name to add prefix
135    let rewritten: Vec<String> = names
136        .into_iter()
137        .map(|name| {
138            if is_unique_name(&name) {
139                to_fake_name(&name, source)
140            } else {
141                name
142            }
143        })
144        .collect();
145
146    // Serialize the new array body
147    let z_endian = match msg.header.endian {
148        Endian::Little => LE,
149        Endian::Big => BE,
150    };
151    let new_body = to_bytes(Context::new_dbus(z_endian, 0), &rewritten)?;
152
153    // Rebuild message with new body, preserving header
154    rebuild_message_with_body(&msg.raw, msg.header.endian, &new_body)
155}
156
157/// Rewrite unique name in request body (for GetConnectionCredentials etc.)
158/// Removes the fake prefix before sending to upstream
159pub fn rewrite_unique_name_request(msg: &Message) -> Result<(Vec<u8>, Bus)> {
160    use crate::fake_name::from_fake_name;
161
162    let body_start = msg.body_start();
163
164    if body_start >= msg.raw.len() {
165        bail!("No body in message");
166    }
167
168    // Parse the string from body
169    let str_len = msg.header.endian.read_u32(&msg.raw[body_start..]) as usize;
170    let str_start = body_start + 4;
171    let str_end = str_start + str_len;
172
173    if str_end > msg.raw.len() {
174        bail!("Invalid string in body");
175    }
176
177    let name = String::from_utf8_lossy(&msg.raw[str_start..str_end]).to_string();
178
179    // Check if it's a fake unique name
180    if let Some((real_name, bus)) = from_fake_name(&name) {
181        let new_raw =
182            rebuild_message_with_string(&msg.raw, msg.header.endian, body_start, &real_name)?;
183        Ok((new_raw, bus))
184    } else {
185        // Not a fake name, return original
186        Ok((msg.raw.clone(), Bus::Sandbox))
187    }
188}
189
190/// Merge ListNames results from both buses
191pub fn merge_list_names(host_names: Vec<String>, sandbox_names: Vec<String>) -> Vec<String> {
192    use std::collections::HashSet;
193
194    let mut seen: HashSet<String> = HashSet::new();
195    let mut result = Vec::new();
196
197    // Add sandbox names first (with :s. prefix for unique names)
198    for name in sandbox_names {
199        let key = if is_unique_name(&name) {
200            to_fake_name(&name, Bus::Sandbox)
201        } else {
202            name.clone()
203        };
204        if seen.insert(key.clone()) {
205            result.push(key);
206        }
207    }
208
209    // Add host names (with :h. prefix for unique names)
210    for name in host_names {
211        let key = if is_unique_name(&name) {
212            to_fake_name(&name, Bus::Host)
213        } else {
214            name.clone()
215        };
216        if seen.insert(key.clone()) {
217            result.push(key);
218        }
219    }
220
221    result
222}
223
224/// Rewrite NameOwnerChanged signal body
225/// Body format: (name: s, old_owner: s, new_owner: s)
226pub fn rewrite_name_owner_changed(msg: &Message, source: Bus) -> Result<Vec<u8>> {
227    let body_start = msg.body_start();
228
229    if body_start >= msg.raw.len() {
230        return Ok(msg.raw.clone());
231    }
232
233    let z_endian = match msg.header.endian {
234        Endian::Little => ZEndian::Little,
235        Endian::Big => ZEndian::Big,
236    };
237
238    // Parse the three strings from body
239    let ctxt = Context::new_dbus(z_endian, body_start);
240    let body_data = &msg.raw[body_start..];
241    let data = Data::new(body_data, ctxt);
242
243    let (name, old_owner, new_owner): (String, String, String) =
244        match data.deserialize::<(String, String, String)>() {
245            Ok((tuple, _)) => tuple,
246            Err(e) => {
247                tracing::warn!("Failed to parse NameOwnerChanged: {}", e);
248                return Ok(msg.raw.clone());
249            }
250        };
251
252    // Rewrite unique names in old_owner and new_owner
253    let new_old_owner = if is_unique_name(&old_owner) && !old_owner.is_empty() {
254        to_fake_name(&old_owner, source)
255    } else {
256        old_owner
257    };
258
259    let new_new_owner = if is_unique_name(&new_owner) && !new_owner.is_empty() {
260        to_fake_name(&new_owner, source)
261    } else {
262        new_owner
263    };
264
265    // Serialize the new body
266    let new_body = match msg.header.endian {
267        Endian::Little => to_bytes(
268            Context::new_dbus(LE, 0),
269            &(name, new_old_owner, new_new_owner),
270        )?,
271        Endian::Big => to_bytes(
272            Context::new_dbus(BE, 0),
273            &(name, new_old_owner, new_new_owner),
274        )?,
275    };
276
277    rebuild_message_with_body(&msg.raw, msg.header.endian, &new_body)
278}
279
280/// Get the body start position in a D-Bus message
281fn get_body_start(raw: &[u8], endian: Endian) -> usize {
282    let fixed_header_size = 12;
283    let array_len = endian.read_u32(&raw[fixed_header_size..]) as usize;
284    let header_end = 16 + array_len;
285    let padding = (8 - (header_end % 8)) % 8;
286    header_end + padding
287}
288
289/// Rebuild a message with a new string in the body
290fn rebuild_message_with_string(
291    raw: &[u8],
292    endian: Endian,
293    _body_start: usize,
294    new_str: &str,
295) -> Result<Vec<u8>> {
296    let new_len = new_str.len() as u32;
297    let new_len_bytes = match endian {
298        Endian::Little => new_len.to_le_bytes(),
299        Endian::Big => new_len.to_be_bytes(),
300    };
301
302    // Build new body: length + string + null terminator
303    let mut new_body = Vec::with_capacity(4 + new_str.len() + 1);
304    new_body.extend_from_slice(&new_len_bytes);
305    new_body.extend_from_slice(new_str.as_bytes());
306    new_body.push(0);
307
308    rebuild_message_with_body(raw, endian, &new_body)
309}
310
311/// Rebuild a message with a new body
312fn rebuild_message_with_body(raw: &[u8], endian: Endian, new_body: &[u8]) -> Result<Vec<u8>> {
313    let body_start = get_body_start(raw, endian);
314
315    // Update body length in header (bytes 4-7)
316    let new_body_len = new_body.len() as u32;
317    let body_len_bytes = match endian {
318        Endian::Little => new_body_len.to_le_bytes(),
319        Endian::Big => new_body_len.to_be_bytes(),
320    };
321
322    let mut new_raw = Vec::with_capacity(body_start + new_body.len());
323    new_raw.extend_from_slice(&raw[..4]); // endian, type, flags, version
324    new_raw.extend_from_slice(&body_len_bytes); // new body length
325    new_raw.extend_from_slice(&raw[8..body_start]); // serial + header fields + padding
326    new_raw.extend_from_slice(new_body);
327
328    Ok(new_raw)
329}
330
331/// Parse a string array from message body (for ListNames response)
332pub fn parse_string_array(msg: &Message) -> Result<Vec<String>> {
333    let body_start = msg.body_start();
334
335    if body_start >= msg.raw.len() {
336        return Ok(vec![]);
337    }
338
339    let z_endian = match msg.header.endian {
340        Endian::Little => ZEndian::Little,
341        Endian::Big => ZEndian::Big,
342    };
343
344    let ctxt = Context::new_dbus(z_endian, body_start);
345    let body_data = &msg.raw[body_start..];
346    let data = Data::new(body_data, ctxt);
347
348    let names: Vec<String> = data
349        .deserialize::<Vec<String>>()
350        .map(|(names, _)| names)
351        .unwrap_or_default();
352
353    Ok(names)
354}
355
356/// Build a ListNames response message
357pub fn build_list_names_response(
358    original_request: &Message,
359    names: Vec<String>,
360) -> Result<Vec<u8>> {
361    let endian = original_request.header.endian;
362
363    // Serialize the names array
364    let body = match endian {
365        Endian::Little => to_bytes(Context::new_dbus(LE, 0), &names)?,
366        Endian::Big => to_bytes(Context::new_dbus(BE, 0), &names)?,
367    };
368
369    // Build response header with signature "as" for array of strings
370    build_method_return(original_request, &body, Some("as"))
371}
372
373/// Build a MethodReturn message
374fn build_method_return(request: &Message, body: &[u8], signature: Option<&str>) -> Result<Vec<u8>> {
375    use zvariant::{Signature, Value};
376
377    let endian = request.header.endian;
378    let z_endian = match endian {
379        Endian::Little => LE,
380        Endian::Big => BE,
381    };
382
383    // Build header fields for MethodReturn
384    let mut fields: Vec<(u8, Value)> = vec![
385        (5, Value::U32(request.header.serial)), // REPLY_SERIAL
386    ];
387
388    // Add signature field if provided
389    if let Some(sig) = signature {
390        fields.push((8, Value::Signature(Signature::from_str_unchecked(sig)))); // SIGNATURE
391    }
392
393    let ctxt = Context::new_dbus(z_endian, 12);
394    let fields_encoded = to_bytes(ctxt, &fields)?;
395    let array_len = (fields_encoded.len() - 4) as u32;
396
397    // Calculate padding
398    let header_end = 16 + array_len as usize;
399    let padding = (8 - (header_end % 8)) % 8;
400
401    // Build message
402    let mut msg = Vec::with_capacity(16 + array_len as usize + padding + body.len());
403
404    // Fixed header
405    msg.push(if endian == Endian::Little { b'l' } else { b'B' });
406    msg.push(2); // MethodReturn
407    msg.push(1); // NO_REPLY_EXPECTED flag
408    msg.push(1); // protocol version
409
410    // Body length
411    let body_len_bytes = match endian {
412        Endian::Little => (body.len() as u32).to_le_bytes(),
413        Endian::Big => (body.len() as u32).to_be_bytes(),
414    };
415    msg.extend_from_slice(&body_len_bytes);
416
417    // Serial (use request serial + 1000000 to avoid collision)
418    let serial = request.header.serial.wrapping_add(1000000);
419    let serial_bytes = match endian {
420        Endian::Little => serial.to_le_bytes(),
421        Endian::Big => serial.to_be_bytes(),
422    };
423    msg.extend_from_slice(&serial_bytes);
424
425    // Header fields
426    msg.extend_from_slice(&fields_encoded);
427
428    // Padding
429    msg.resize(msg.len() + padding, 0);
430
431    // Body
432    msg.extend_from_slice(body);
433
434    Ok(msg)
435}
436
437#[cfg(test)]
438mod tests {
439    use super::*;
440
441    #[test]
442    fn test_merge_list_names() {
443        let host = vec![
444            "org.fcitx.Fcitx5".to_string(),
445            ":1.45".to_string(),
446            "org.freedesktop.DBus".to_string(),
447        ];
448        let sandbox = vec![
449            "org.example.App".to_string(),
450            ":1.23".to_string(),
451            "org.freedesktop.DBus".to_string(), // duplicate
452        ];
453
454        let merged = merge_list_names(host, sandbox);
455
456        // Should have: org.example.App, :s.1.23, org.freedesktop.DBus, org.fcitx.Fcitx5, :h.1.45
457        assert!(merged.contains(&"org.example.App".to_string()));
458        assert!(merged.contains(&":s.1.23".to_string()));
459        assert!(merged.contains(&"org.freedesktop.DBus".to_string()));
460        assert!(merged.contains(&"org.fcitx.Fcitx5".to_string()));
461        assert!(merged.contains(&":h.1.45".to_string()));
462
463        // org.freedesktop.DBus should only appear once
464        assert_eq!(
465            merged
466                .iter()
467                .filter(|n| *n == "org.freedesktop.DBus")
468                .count(),
469            1
470        );
471    }
472
473    #[test]
474    fn test_needs_checks() {
475        assert!(needs_response_rewrite("GetNameOwner"));
476        assert!(needs_response_rewrite("Hello"));
477        assert!(!needs_response_rewrite("RequestName"));
478
479        assert!(needs_request_rewrite("GetConnectionCredentials"));
480        assert!(needs_request_rewrite("GetNameOwner"));
481        assert!(needs_request_rewrite("NameHasOwner"));
482
483        assert!(needs_merge("ListNames"));
484        assert!(!needs_merge("GetNameOwner"));
485
486        assert!(signal_needs_rewrite("NameOwnerChanged"));
487        assert!(!signal_needs_rewrite("NameAcquired"));
488    }
489}