privilege_rs/
lib.rs

1use eframe::egui;
2use std::process::{Command, Stdio};
3
4#[derive(PartialEq)]
5pub enum Privilege {
6    Root,
7    User,
8    Suid,
9}
10
11/// Check current privilege:
12///  - If Root/Suid privilege -> donothing
13///  - If User privilege -> password request and re-run the application.
14pub 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]), // Adjusted size
19                ..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                    // Center the content
28                    ui.vertical_centered(|ui| {
29                        ui.heading("Privilege Request");
30
31                        ui.add_space(20.0); // Add space for better layout
32
33                        // Password input section
34                        ui.horizontal(|ui| {
35                            let name_label = ui.label("Enter admin/root password: ");
36                            ui.add_space(5.0); // Add space between label and input
37                            ui.add(egui::TextEdit::singleline(&mut password).password(true))
38                                .labelled_by(name_label.id);
39                        });
40
41                        ui.add_space(10.0); // Add space before the button
42
43                        // Submit button
44                        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                                // close the app
50                            } else {
51                                notification = "Failed to verify the password.".to_owned();
52                            }
53                        }
54
55                        // Notification display
56                        if !notification.is_empty() {
57                            ui.add_space(15.0); // Add space before notification
58                            ui.label(egui::RichText::new(&notification).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
75/// Use getuid() and geteuid() to know current privilege
76fn 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
87/// Run a dummy command with the password first to verify
88fn verify_password(password: &str) -> bool {
89    let mut command = Command::new("sudo");
90    let mut child = command
91        .arg("-k") // Invalidate cached credentials
92        .arg("-S") // Read password from stdin
93        .arg("true") // Dummy command to verify password
94        .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
110/// Re-run the application with sudo password
111fn 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        // Close stdin to signal no more input will be provided
133    }
134    std::process::exit(0);
135}