1use eframe::egui;
2use std::process::{Command, Stdio};
3
4#[derive(PartialEq)]
5pub enum Privilege {
6 Root,
7 User,
8 Suid,
9}
10
11pub fn privilege_request() -> std::io::Result<Privilege> {
15 match get_privilege() {
16 Privilege::User => {
17 let options = eframe::NativeOptions {
18 viewport: egui::ViewportBuilder::default().with_inner_size([400.0, 300.0]), ..Default::default()
20 };
21
22 let mut password = String::new();
23 let mut notification = String::new();
24
25 let _ = eframe::run_simple_native("Privilege Request", options, move |ctx, _frame| {
26 egui::CentralPanel::default().show(ctx, |ui| {
27 ui.vertical_centered(|ui| {
29 ui.heading("Privilege Request");
30
31 ui.add_space(20.0); ui.horizontal(|ui| {
35 let name_label = ui.label("Enter admin/root password: ");
36 ui.add_space(5.0); ui.add(egui::TextEdit::singleline(&mut password).password(true))
38 .labelled_by(name_label.id);
39 });
40
41 ui.add_space(10.0); if ui.button("Submit").clicked()
45 || ui.input(|i| i.key_pressed(egui::Key::Enter))
46 {
47 if verify_password(&password) {
48 run_with_privilege(password.clone());
49 } else {
51 notification = "Failed to verify the password.".to_owned();
52 }
53 }
54
55 if !notification.is_empty() {
57 ui.add_space(15.0); ui.label(egui::RichText::new(¬ification).color(egui::Color32::RED));
59 }
60 });
61 });
62 });
63 Ok(Privilege::User)
64 }
65 Privilege::Suid => {
66 unsafe {
67 libc::setuid(0);
68 }
69 Ok(Privilege::Suid)
70 }
71 Privilege::Root => Ok(Privilege::Root),
72 }
73}
74
75fn get_privilege() -> Privilege {
77 let uid = unsafe { libc::getuid() };
78 let euid = unsafe { libc::geteuid() };
79
80 match (uid, euid) {
81 (0, 0) => Privilege::Root,
82 (_, 0) => Privilege::Suid,
83 (_, _) => Privilege::User,
84 }
85}
86
87fn verify_password(password: &str) -> bool {
89 let mut command = Command::new("sudo");
90 let mut child = command
91 .arg("-k") .arg("-S") .arg("true") .stdin(Stdio::piped())
95 .stdout(Stdio::null())
96 .stderr(Stdio::null())
97 .spawn()
98 .expect("Failed to execute sudo");
99
100 if let Some(mut stdin) = child.stdin.take() {
101 use std::io::Write;
102 writeln!(stdin, "{}", password).expect("Failed to write to stdin");
103 }
104
105 let ecode = child.wait().expect("Failed to wait on child");
106
107 ecode.success()
108}
109
110fn run_with_privilege(pwd: String) {
112 println!("Escalating privileges");
113 let mut args: Vec<_> = std::env::args().collect();
114 if let Some(absolute_path) = std::env::current_exe()
115 .ok()
116 .and_then(|p| p.to_str().map(|p| p.to_string()))
117 {
118 args[0] = absolute_path;
119 }
120
121 let mut command: Command = Command::new("sudo");
122 let mut child = command
123 .arg("-S")
124 .args(args)
125 .stdin(Stdio::piped())
126 .spawn()
127 .expect("failed to execute child");
128
129 if let Some(mut stdin) = child.stdin.take() {
130 use std::io::Write;
131 writeln!(stdin, "{}", pwd).expect("Failed to write to stdin");
132 }
134 std::process::exit(0);
135}