diff --git a/idevice/src/lib.rs b/idevice/src/lib.rs index 3c6d03e..26175f2 100644 --- a/idevice/src/lib.rs +++ b/idevice/src/lib.rs @@ -294,6 +294,10 @@ pub enum IdeviceError { #[error("image not mounted")] ImageNotMounted, + #[cfg(feature = "misagent")] + #[error("misagent operation failed")] + MisagentFailure, + #[cfg(any(feature = "tss", feature = "tunneld"))] #[error("http reqwest error")] Reqwest(#[from] reqwest::Error), diff --git a/idevice/src/misagent.rs b/idevice/src/misagent.rs index d402269..7718287 100644 --- a/idevice/src/misagent.rs +++ b/idevice/src/misagent.rs @@ -2,7 +2,7 @@ // Incomplete implementation for installation_proxy use log::warn; -use plist::{Dictionary, Value}; +use plist::Dictionary; use crate::{lockdownd::LockdowndClient, Idevice, IdeviceError, IdeviceService}; @@ -45,39 +45,6 @@ impl MisagentClient { req.insert("MessageType".into(), "Install".into()); req.insert("Profile".into(), plist::Value::Data(profile)); - // TODO: Determine if there are other types of profiles we can install - req.insert("ProfileType".into(), "Provisioning".into()); - - self.idevice - .send_plist(plist::Value::Dictionary(req)) - .await?; - - let res = self.idevice.read_plist().await?; - - todo!(); - } - - pub async fn remove(&mut self, id: &str) -> Result<(), IdeviceError> { - let mut req = Dictionary::new(); - req.insert("MessageType".into(), "Remove".into()); - req.insert("ProfileID".into(), id.into()); - - // TODO: Determine if there are other types of profiles we can install - req.insert("ProfileType".into(), "Provisioning".into()); - - self.idevice - .send_plist(plist::Value::Dictionary(req)) - .await?; - - let res = self.idevice.read_plist().await?; - todo!() - } - - pub async fn copy_all(&mut self) -> Result, IdeviceError> { - let mut req = Dictionary::new(); - req.insert("MessageType".into(), "CopyAll".into()); - - // TODO: Determine if there are other types of profiles we can install req.insert("ProfileType".into(), "Provisioning".into()); self.idevice @@ -85,13 +52,88 @@ impl MisagentClient { .await?; let mut res = self.idevice.read_plist().await?; - Ok(match res.remove("Payload") { - // TODO: Determine if this is actually an array - Some(plist::Value::Array(a)) => a, + + match res.remove("Status") { + Some(plist::Value::Integer(status)) => { + if let Some(status) = status.as_unsigned() { + if status == 1 { + Ok(()) + } else { + Err(IdeviceError::MisagentFailure) + } + } else { + warn!("Misagent return status wasn't unsigned"); + Err(IdeviceError::UnexpectedResponse) + } + } + _ => { + warn!("Did not get integer status response"); + Err(IdeviceError::UnexpectedResponse) + } + } + } + + pub async fn remove(&mut self, id: &str) -> Result<(), IdeviceError> { + let mut req = Dictionary::new(); + req.insert("MessageType".into(), "Remove".into()); + req.insert("ProfileID".into(), id.into()); + + req.insert("ProfileType".into(), "Provisioning".into()); + + self.idevice + .send_plist(plist::Value::Dictionary(req)) + .await?; + + let mut res = self.idevice.read_plist().await?; + + match res.remove("Status") { + Some(plist::Value::Integer(status)) => { + if let Some(status) = status.as_unsigned() { + if status == 1 { + Ok(()) + } else { + Err(IdeviceError::MisagentFailure) + } + } else { + warn!("Misagent return status wasn't unsigned"); + Err(IdeviceError::UnexpectedResponse) + } + } + _ => { + warn!("Did not get integer status response"); + Err(IdeviceError::UnexpectedResponse) + } + } + } + + pub async fn copy_all(&mut self) -> Result>, IdeviceError> { + let mut req = Dictionary::new(); + req.insert("MessageType".into(), "CopyAll".into()); + + req.insert("ProfileType".into(), "Provisioning".into()); + + self.idevice + .send_plist(plist::Value::Dictionary(req)) + .await?; + + let mut res = self.idevice.read_plist().await?; + match res.remove("Payload") { + Some(plist::Value::Array(a)) => { + let mut res = Vec::new(); + for profile in a { + if let Some(profile) = profile.as_data() { + res.push(profile.to_vec()); + } else { + warn!("Misagent CopyAll did not return data plists"); + return Err(IdeviceError::UnexpectedResponse); + } + } + Ok(res) + } _ => { warn!("Did not get a payload of provisioning profiles as an array"); - return Err(IdeviceError::UnexpectedResponse); + Err(IdeviceError::UnexpectedResponse) } - }) + } } } diff --git a/tools/src/heartbeat_client.rs b/tools/src/heartbeat_client.rs index a04bf21..450181a 100644 --- a/tools/src/heartbeat_client.rs +++ b/tools/src/heartbeat_client.rs @@ -61,7 +61,7 @@ async fn main() { let mut interval = 15; loop { - interval = heartbeat_client.get_marco(interval).await.unwrap(); + interval = heartbeat_client.get_marco(1000).await.unwrap(); heartbeat_client.send_polo().await.unwrap(); } } diff --git a/tools/src/misagent.rs b/tools/src/misagent.rs index 869d475..4dafd38 100644 --- a/tools/src/misagent.rs +++ b/tools/src/misagent.rs @@ -1,7 +1,9 @@ // Jackson Coxson -use clap::{Arg, Command}; -use idevice::{misagent::MisagentClient, pretty_print_plist, IdeviceService}; +use std::path::PathBuf; + +use clap::{arg, value_parser, Arg, Command}; +use idevice::{misagent::MisagentClient, IdeviceService}; mod common; @@ -26,8 +28,7 @@ async fn main() { .arg( Arg::new("udid") .value_name("UDID") - .help("UDID of the device (overrides host/pairing file)") - .index(1), + .help("UDID of the device (overrides host/pairing file)"), ) .arg( Arg::new("about") @@ -35,7 +36,19 @@ async fn main() { .help("Show about information") .action(clap::ArgAction::SetTrue), ) - .subcommand(Command::new("list").about("Lists the images mounted on the device")) + .subcommand( + Command::new("list") + .about("Lists the images mounted on the device") + .arg( + arg!(-s --save "the folder to save the profiles to") + .value_parser(value_parser!(PathBuf)), + ), + ) + .subcommand( + Command::new("remove") + .about("Remove a provisioning profile") + .arg(Arg::new("id").required(true).index(1)), + ) .get_matches(); if matches.get_flag("about") { @@ -59,14 +72,29 @@ async fn main() { .await .expect("Unable to connect to misagent"); - if matches.subcommand_matches("list").is_some() { + if let Some(matches) = matches.subcommand_matches("list") { let images = misagent_client .copy_all() .await .expect("Unable to get images"); - for i in images { - println!("{}", pretty_print_plist(&i)); + for i in &images { + // println!("{:?}", i); } + if let Some(path) = matches.get_one::("save") { + tokio::fs::create_dir_all(path) + .await + .expect("Unable to create save DIR"); + + for (index, image) in images.iter().enumerate() { + let f = path.join(format!("{index}.pem")); + tokio::fs::write(f, image) + .await + .expect("Failed to write image"); + } + } + } else if let Some(matches) = matches.subcommand_matches("remove") { + let id = matches.get_one::("id").expect("No ID passed"); + misagent_client.remove(id).await.expect("Failed to remove"); } else { eprintln!("Invalid usage, pass -h for help"); }