Compare commits
2 commits
bf2a95d8db
...
cbb4a68c9f
Author | SHA1 | Date | |
---|---|---|---|
cbb4a68c9f | |||
75f5bf8f73 |
3 changed files with 103 additions and 83 deletions
|
@ -75,4 +75,8 @@ impl WlObjects {
|
||||||
pub fn lookup_global(&self, name: u32) -> Option<&str> {
|
pub fn lookup_global(&self, name: u32) -> Option<&str> {
|
||||||
self.global_names.get(&name).map(|s| s.as_str())
|
self.global_names.get(&name).map(|s| s.as_str())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_global(&mut self, name: u32) {
|
||||||
|
self.global_names.remove(&name);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
12
src/proto.rs
12
src/proto.rs
|
@ -25,18 +25,6 @@ macro_rules! bubble_malformed {
|
||||||
}};
|
}};
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! match_decoded {
|
|
||||||
(match $decoded:ident {$($t:ty => $act:block$(,)?)+}) => {
|
|
||||||
if let crate::proto::WaylandProtocolParsingOutcome::Ok($decoded) = $decoded {
|
|
||||||
$(
|
|
||||||
if let Some($decoded) = $decoded.downcast_ref::<$t>() {
|
|
||||||
$act
|
|
||||||
}
|
|
||||||
)+
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Eq)]
|
#[derive(PartialEq, Eq)]
|
||||||
pub enum WlMsgType {
|
pub enum WlMsgType {
|
||||||
Request,
|
Request,
|
||||||
|
|
80
src/state.rs
80
src/state.rs
|
@ -7,8 +7,9 @@ use crate::{
|
||||||
config::Config,
|
config::Config,
|
||||||
objects::WlObjects,
|
objects::WlObjects,
|
||||||
proto::{
|
proto::{
|
||||||
WL_REGISTRY, WlDisplayDeleteIdEvent, WlDisplayGetRegistryRequest, WlRegistryBindRequest,
|
WL_REGISTRY, WaylandProtocolParsingOutcome, WlDisplayDeleteIdEvent,
|
||||||
WlRegistryGlobalEvent,
|
WlDisplayGetRegistryRequest, WlRegistryBindRequest, WlRegistryGlobalEvent,
|
||||||
|
WlRegistryGlobalRemoveEvent,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,8 +28,11 @@ impl WlMitmState {
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn on_c2s_request(&mut self, raw_msg: &WlRawMsg) -> bool {
|
pub fn on_c2s_request(&mut self, raw_msg: &WlRawMsg) -> bool {
|
||||||
let msg = crate::proto::decode_request(&self.objects, raw_msg);
|
let msg = match crate::proto::decode_request(&self.objects, raw_msg) {
|
||||||
if let crate::proto::WaylandProtocolParsingOutcome::MalformedMessage = msg {
|
WaylandProtocolParsingOutcome::Ok(msg) => msg,
|
||||||
|
WaylandProtocolParsingOutcome::MalformedMessage => {
|
||||||
|
// Kill all malformed messages
|
||||||
|
// Note that they are different from messages whose object / message types are unknown
|
||||||
error!(
|
error!(
|
||||||
obj_id = raw_msg.obj_id,
|
obj_id = raw_msg.obj_id,
|
||||||
opcode = raw_msg.opcode,
|
opcode = raw_msg.opcode,
|
||||||
|
@ -37,19 +41,36 @@ impl WlMitmState {
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
match_decoded! {
|
// Pass through all unknown messages -- they could be from a Wayland protocol we haven't
|
||||||
match msg {
|
// been built against!
|
||||||
WlDisplayGetRegistryRequest => {
|
// Note that this won't pass through messages for globals we haven't allowed:
|
||||||
self.objects.record_object(WL_REGISTRY, msg.registry);
|
// to use a global, a client must first _bind_ that global, and _that_ message is intercepted
|
||||||
|
// below. There, we match based on the textual representation of the interface, so it works
|
||||||
|
// even for globals from protocols we don't know.
|
||||||
|
// It does mean we can't filter against methods that create more objects _from_ that
|
||||||
|
// global, though.
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
WlRegistryBindRequest => {
|
};
|
||||||
|
|
||||||
|
if let Some(msg) = msg.downcast_ref::<WlDisplayGetRegistryRequest>() {
|
||||||
|
self.objects.record_object(WL_REGISTRY, msg.registry);
|
||||||
|
} else if let Some(msg) = msg.downcast_ref::<WlRegistryBindRequest>() {
|
||||||
|
// If we have blocked this global, this lookup should return None, thus blocking client attempts
|
||||||
|
// to bind to a blocked global.
|
||||||
|
// Note that because we've removed said global from the registry, a client _SHOULD NOT_ be attempting
|
||||||
|
// to bind to it; if it does, it's likely a malicious client!
|
||||||
|
// So, we simply remove these messages from the stream, which will cause the Wayland server to error out.
|
||||||
let Some(interface) = self.objects.lookup_global(msg.name) else {
|
let Some(interface) = self.objects.lookup_global(msg.name) else {
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
|
|
||||||
if interface != msg.id_interface_name {
|
if interface != msg.id_interface_name {
|
||||||
error!("Client binding to interface {}, but the interface name {} should correspond to {}", msg.id_interface_name, msg.name, interface);
|
error!(
|
||||||
|
"Client binding to interface {}, but the interface name {} should correspond to {}",
|
||||||
|
msg.id_interface_name, msg.name, interface
|
||||||
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,27 +84,32 @@ impl WlMitmState {
|
||||||
self.objects.record_object(t, msg.id);
|
self.objects.record_object(t, msg.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip_all)]
|
#[tracing::instrument(skip_all)]
|
||||||
pub fn on_s2c_event(&mut self, raw_msg: &WlRawMsg) -> bool {
|
pub fn on_s2c_event(&mut self, raw_msg: &WlRawMsg) -> bool {
|
||||||
let msg = crate::proto::decode_event(&self.objects, raw_msg);
|
let msg = match crate::proto::decode_event(&self.objects, raw_msg) {
|
||||||
if let crate::proto::WaylandProtocolParsingOutcome::MalformedMessage = msg {
|
WaylandProtocolParsingOutcome::Ok(msg) => msg,
|
||||||
|
WaylandProtocolParsingOutcome::MalformedMessage => {
|
||||||
error!(
|
error!(
|
||||||
obj_id = raw_msg.obj_id,
|
obj_id = raw_msg.obj_id,
|
||||||
opcode = raw_msg.opcode,
|
opcode = raw_msg.opcode,
|
||||||
|
num_fds = raw_msg.fds.len(),
|
||||||
"Malformed event"
|
"Malformed event"
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
_ => {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
match_decoded! {
|
if let Some(msg) = msg.downcast_ref::<WlRegistryGlobalEvent>() {
|
||||||
match msg {
|
// This event is how Wayland servers announce globals -- and they are the entrypoint to
|
||||||
WlRegistryGlobalEvent => {
|
// most extensions! You need at least one global registered for clients to be able to
|
||||||
|
// access methods from that extension; but those methods _could_ create more objects.
|
||||||
debug!(
|
debug!(
|
||||||
interface = msg.interface,
|
interface = msg.interface,
|
||||||
name = msg.name,
|
name = msg.name,
|
||||||
|
@ -91,8 +117,7 @@ impl WlMitmState {
|
||||||
"got global"
|
"got global"
|
||||||
);
|
);
|
||||||
|
|
||||||
self.objects.record_global(msg.name, msg.interface);
|
// To block entire extensions, we just need to filter out their announced global objects.
|
||||||
|
|
||||||
if !self.config.filter.allowed_globals.contains(msg.interface) {
|
if !self.config.filter.allowed_globals.contains(msg.interface) {
|
||||||
info!(
|
info!(
|
||||||
interface = msg.interface,
|
interface = msg.interface,
|
||||||
|
@ -100,14 +125,17 @@ impl WlMitmState {
|
||||||
);
|
);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
WlDisplayDeleteIdEvent => {
|
// Else, record the global object. These are the only ones we're ever going to allow through.
|
||||||
// When an object is acknowledged to be deleted, remove it from our
|
// We block bind requests on any interface that's not recorded here.
|
||||||
// internal cache of all registered objects
|
self.objects.record_global(msg.name, msg.interface);
|
||||||
|
} else if let Some(msg) = msg.downcast_ref::<WlRegistryGlobalRemoveEvent>() {
|
||||||
|
// Remove globals that the server has removed
|
||||||
|
self.objects.remove_global(msg.name);
|
||||||
|
} else if let Some(msg) = msg.downcast_ref::<WlDisplayDeleteIdEvent>() {
|
||||||
|
// Server has acknowledged deletion of an object
|
||||||
self.objects.remove_object(msg.id);
|
self.objects.remove_object(msg.id);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue