Skip to content

Commit

Permalink
backdoor: allow ignoring iopl permission errors
Browse files Browse the repository at this point in the history
This introduces an additional flag for the backdoor-opening logic,
in order to allow consuming applications to keep going when changing
I/O access level is not alloewd (see kernel_lockdown(7) on Linux).
In most cases the library may work without higher I/O level,
and actual failures can be handled via normal error-handling on RPC
methods.
  • Loading branch information
lucab committed Oct 6, 2020
1 parent 999c62a commit 5a90030
Show file tree
Hide file tree
Showing 6 changed files with 37 additions and 16 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "vmw_backdoor"
version = "0.1.4-alpha.0"
version = "0.2.0-alpha.0"
authors = ["Luca BRUNO <[email protected]>"]
edition = "2018"
license = "MIT/Apache-2.0"
Expand Down
2 changes: 1 addition & 1 deletion examples/check-backdoor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ fn main() {
let is_vmw = vmw::is_vmware_cpu();
println!("VMware CPU detected: {}.", is_vmw);

let mut backdoor = vmw::access_backdoor().unwrap();
let mut backdoor = vmw::access_backdoor(false).unwrap();
println!("Raised I/O access to reach backdoor port.");

let found = match backdoor.probe_vmware_backdoor() {
Expand Down
2 changes: 1 addition & 1 deletion examples/get-guestinfo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn main() {
panic!("Hypervisor not present");
}

let mut backdoor = vmw::probe_backdoor().unwrap();
let mut backdoor = vmw::probe_backdoor(false).unwrap();
eprintln!("Got backdoor access.");

let mut erpc = backdoor.open_enhanced_chan().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion examples/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ fn main() {
panic!("Hypervisor not present");
}

let mut backdoor = vmw::probe_backdoor().unwrap();
let mut backdoor = vmw::probe_backdoor(false).unwrap();
eprintln!("Got backdoor access.");

let mut erpc = backdoor.open_enhanced_chan().unwrap();
Expand Down
2 changes: 1 addition & 1 deletion examples/report-agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ fn main() {
panic!("Hypervisor not present");
}

let mut backdoor = vmw::probe_backdoor().unwrap();
let mut backdoor = vmw::probe_backdoor(false).unwrap();
eprintln!("Got backdoor access.");

let mut erpc = backdoor.open_enhanced_chan().unwrap();
Expand Down
43 changes: 32 additions & 11 deletions src/backdoor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ pub(crate) const COMMAND_ERPC: u32 = 0x1E;
/// Try to acquire access to the backdoor, but do NOT probe its presence.
///
/// On Linux, this requires running with `CAP_SYS_RAWIO` privileges.
pub fn access_backdoor() -> Result<BackdoorGuard, VmwError> {
BackdoorGuard::change_io_access(true)?;
pub fn access_backdoor(ignore_access_errors: bool) -> Result<BackdoorGuard, VmwError> {
let changed = BackdoorGuard::change_io_access(true, ignore_access_errors)?;
Ok(BackdoorGuard {
release_on_drop: true,
release_on_drop: changed,
})
}

/// Try to acquire access to the backdoor, and probe its presence.
///
/// On Linux, this requires running with `CAP_SYS_RAWIO` privileges.
pub fn probe_backdoor() -> Result<BackdoorGuard, VmwError> {
BackdoorGuard::change_io_access(true)?;
pub fn probe_backdoor(ignore_access_errors: bool) -> Result<BackdoorGuard, VmwError> {
let changed = BackdoorGuard::change_io_access(true, ignore_access_errors)?;
let mut guard = BackdoorGuard {
release_on_drop: true,
release_on_drop: changed,
};
guard.probe_vmware_backdoor()?;
Ok(guard)
Expand All @@ -55,7 +55,7 @@ impl BackdoorGuard {
/// Try to release backdoor access.
pub fn release_access(self) -> Result<(), Self> {
let mut guard = self;
if Self::change_io_access(false).is_err() {
if Self::change_io_access(false, false).is_err() {
return Err(guard);
}

Expand All @@ -69,24 +69,45 @@ impl BackdoorGuard {
EnhancedChan::open(self)
}

pub(crate) fn change_io_access(acquire: bool) -> Result<(), VmwError> {
/// Try to change I/O ports access level.
///
/// This returns whether access level was changed, so that it can
/// be reverted later on.
pub(crate) fn change_io_access(
acquire: bool,
ignore_access_errors: bool,
) -> Result<bool, VmwError> {
// NOTE(lucab): `ioperm()` is not enough here, as the backdoor
// protocol uses a dynamic range of I/O ports.
let level = if acquire { 0b11 } else { 0b00 };
let err = unsafe { libc::iopl(level) };
if err != 0 {
let err_code = errno::errno();

// Ignore permission errors if asked to do so (e.g. because
// of kernel_lockdown).
if ignore_access_errors && err_code.0 == libc::EPERM {
log::warn!(
"ignoring iopl failure: {} (errno: {})",
err_code,
err_code.0
);
return Ok(false);
}

return Err(format!("iopl failed: {} (errno: {})", err_code, err_code.0).into());
};

Ok(())
Ok(true)
}
}

impl Drop for BackdoorGuard {
fn drop(&mut self) {
if self.release_on_drop && Self::change_io_access(false).is_err() {
log::warn!("failed to release backdoor access");
if self.release_on_drop {
if let Err(e) = Self::change_io_access(false, false) {
log::error!("failed to release backdoor access: {}", e);
}
}
}
}
Expand Down

0 comments on commit 5a90030

Please sign in to comment.