- The agent prompt's username field is user-editable. It was passed straight to `auth::authenticate`, so a request scoped to specific accounts (e.g. root only) could have its PAM conversation redirected to any local user. `resolve_user` now accepts only the `unix-user` identities from `BeginAuthentication` (empty = the prefilled default); anything else re-shows the prompt with an explanation. PAM still has to clear polkit's own authorization, but this closes the foot-gun at the one place the identity list is known. - A failed single-instance lock now exits(1) instead of continuing: a second agent would `serve_at` the same object path, and a prompt held by a process that couldn't take the lock is ambiguous state.
99 lines
3 KiB
Rust
99 lines
3 KiB
Rust
//! bread-polkit — themed PolicyKit authentication agent.
|
|
//!
|
|
//! Registers on the `org.freedesktop.PolicyKit1.AuthenticationAgent`
|
|
//! interface and shows a bread-theme GTK4 password prompt. This is an
|
|
//! agent, not a wrapper that execs `polkit-gnome`.
|
|
//!
|
|
//! Autostart: copy `contrib/bread-polkit.desktop` to
|
|
//! `~/.config/autostart/`, or add `exec-once = bread-polkit` to Hyprland.
|
|
|
|
mod agent;
|
|
mod auth;
|
|
mod ui;
|
|
|
|
use bread_app::singleton::Acquire;
|
|
use gtk4::prelude::*;
|
|
|
|
const APP_NAME: &str = "bread-polkit";
|
|
|
|
fn main() {
|
|
let arg = std::env::args().nth(1);
|
|
match arg.as_deref() {
|
|
Some("-h") | Some("--help") => {
|
|
print_help();
|
|
return;
|
|
}
|
|
Some("-V") | Some("--version") => {
|
|
println!("bread-polkit {}", env!("CARGO_PKG_VERSION"));
|
|
return;
|
|
}
|
|
Some(other) => {
|
|
eprintln!("bread-polkit: unknown argument '{other}'");
|
|
print_help();
|
|
std::process::exit(2);
|
|
}
|
|
None => {}
|
|
}
|
|
|
|
tracing_subscriber::fmt()
|
|
.with_env_filter(
|
|
tracing_subscriber::EnvFilter::try_from_default_env()
|
|
.unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")),
|
|
)
|
|
.with_target(false)
|
|
.init();
|
|
|
|
let _guard = match bread_app::try_acquire(APP_NAME) {
|
|
Ok(Acquire::Acquired(g)) => Some(g),
|
|
Ok(Acquire::HeldByOther(pid)) => {
|
|
eprintln!("bread-polkit: already running (pid {pid:?})");
|
|
std::process::exit(0);
|
|
}
|
|
Err(e) => {
|
|
// Don't keep running without the single-instance lock: a second
|
|
// copy would attempt to `serve_at` the same PolicyKit agent
|
|
// object path on the system bus, and a password prompt held by a
|
|
// process whose lock couldn't be taken is ambiguous state. Fail
|
|
// fast and let a wrapper/autostart retry.
|
|
eprintln!("bread-polkit: singleton lock unavailable ({e}); exiting");
|
|
std::process::exit(1);
|
|
}
|
|
};
|
|
|
|
let app_id = bread_app::application_id(APP_NAME).expect("static app name");
|
|
let app = gtk4::Application::builder().application_id(&app_id).build();
|
|
|
|
app.connect_activate(|app| {
|
|
bread_theme::gtk::apply_shared();
|
|
bread_theme::gtk::apply_app_css(ui::app_css);
|
|
// No window until polkit asks; hold so GApplication stays alive.
|
|
std::mem::forget(app.hold());
|
|
if let Err(e) = agent::spawn() {
|
|
eprintln!("bread-polkit: {e:#}");
|
|
app.quit();
|
|
}
|
|
});
|
|
|
|
app.run();
|
|
}
|
|
|
|
fn print_help() {
|
|
print!(
|
|
"\
|
|
bread-polkit — themed PolicyKit authentication agent
|
|
|
|
Usage:
|
|
bread-polkit
|
|
bread-polkit --help
|
|
bread-polkit --version
|
|
|
|
Autostart (pick one):
|
|
cp contrib/bread-polkit.desktop ~/.config/autostart/
|
|
exec-once = bread-polkit # Hyprland
|
|
|
|
The agent talks to the polkit1 AuthenticationAgent API and prompts for
|
|
a password. It does not exec polkit-gnome. Not a bakery product; not
|
|
on the BOS ISO lockfile.
|
|
"
|
|
);
|
|
}
|