From 49394b3e531aed555886b250de6bfeca43d0a6e5 Mon Sep 17 00:00:00 2001 From: "Brian J. Tarricone" Date: Sun, 14 Aug 2022 22:17:58 -0700 Subject: [PATCH] Use PAM properly, which allows us to handle other auth types Previously I was just presenting a static username/password box, and then running PAM with pre-set credentials. This works just fine when PAM is expecting a username and password, but if it's expecting something like a fingerprint scan or a hardware security token, this wouldn't entirely work right. Well, it would "work", but the username/password dialog would be displayed, and then hitting "Unlock" would start a different auth process with no visible feedback as to what's supposed to happen. This also means I need to switch PAM wrapper crates; the one I was using before did not allow passing a fixed username to the underlying pam_start() call, which meant that PAM would try to prompt the user for it, which is not what we want. --- Cargo.lock | 160 +++++++++++++++--- debian/control | 1 + dialog-gtk3/Cargo.toml | 2 +- dialog-gtk3/src/main.rs | 363 +++++++++++++++++++++++----------------- 4 files changed, 351 insertions(+), 175 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1944653..9a537ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -248,7 +248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2da379dbebc0b76ef63ca68d8fc6e71c0f13e59432e0987e508c1820e6ab5239" dependencies = [ "bitflags", - "cexpr", + "cexpr 0.4.0", "clang-sys", "clap 2.34.0", "env_logger 0.8.4", @@ -260,8 +260,31 @@ dependencies = [ "quote", "regex", "rustc-hash", - "shlex", - "which", + "shlex 0.1.1", + "which 3.1.1", +] + +[[package]] +name = "bindgen" +version = "0.59.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +dependencies = [ + "bitflags", + "cexpr 0.6.0", + "clang-sys", + "clap 2.34.0", + "env_logger 0.9.0", + "lazy_static", + "lazycell", + "log", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex 1.1.0", + "which 4.2.5", ] [[package]] @@ -339,7 +362,7 @@ dependencies = [ "gtk", "gtk-sys", "log", - "pam", + "pam-client", "shell-words", ] @@ -442,7 +465,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" dependencies = [ - "nom", + "nom 5.1.2", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.1", ] [[package]] @@ -589,6 +621,23 @@ version = "3.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6907e25393cdcc1f4f3f513d9aac1e840eb1cc341a0fccb01171f7d14d10b946" +[[package]] +name = "either" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" + +[[package]] +name = "enum-repr" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bad30c9c0fa1aaf1ae5010dab11f1117b15d35faf62cda4bbbc53b9987950f18" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "enumflags2" version = "0.7.5" @@ -1090,6 +1139,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "itoa" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c8af84674fe1f223a982c933a0ee1086ac4d4052aa0fb8060c12c6ad838e754" + [[package]] name = "js-sys" version = "0.3.57" @@ -1182,6 +1237,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "nix" version = "0.23.1" @@ -1205,6 +1266,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -1257,22 +1328,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "029d8d0b2f198229de29dca79676f2738ff952edf3fde542eb8bf94d8c21b435" [[package]] -name = "pam" -version = "0.7.0" +name = "pam-client" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa2bdc959c201c047004a1420a92aaa1dd1a6b64d5ef333aa3a4ac764fb93097" +checksum = "51bd776116a7ada5ebbe31f54cdc5b1030ed7265686cf7c8a21c057a2f8dab9a" dependencies = [ + "bitflags", + "enum-repr", "libc", "pam-sys", - "users", + "rpassword", + "rustversion", ] [[package]] name = "pam-sys" -version = "0.5.6" +version = "1.0.0-alpha4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd4858311a097f01a0006ef7d0cd50bca81ec430c949d7bf95cbefd202282434" +checksum = "5e9dfd42858f6a6bb1081079fd9dc259ca3e2aaece6cb689fd36b1058046c969" dependencies = [ + "bindgen 0.59.2", "libc", ] @@ -1558,6 +1633,18 @@ dependencies = [ "winapi", ] +[[package]] +name = "rpassword" +version = "6.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bf099a1888612545b683d2661a1940089f6c2e5a8e38979b2159da876bfd956" +dependencies = [ + "libc", + "serde", + "serde_json", + "winapi", +] + [[package]] name = "rustc-hash" version = "1.1.0" @@ -1573,6 +1660,18 @@ dependencies = [ "semver", ] +[[package]] +name = "rustversion" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8" + +[[package]] +name = "ryu" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4501abdff3ae82a1c1b477a17252eb69cee9e66eb915c1abaa4f44d873df9f09" + [[package]] name = "scopeguard" version = "1.1.0" @@ -1617,6 +1716,17 @@ dependencies = [ "syn", ] +[[package]] +name = "serde_json" +version = "1.0.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38dd04e3c8279e75b31ef29dbdceebfe5ad89f4d0937213c53f7d49d01b3d5a7" +dependencies = [ + "itoa", + "ryu", + "serde", +] + [[package]] name = "serde_repr" version = "0.1.8" @@ -1655,6 +1765,12 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + [[package]] name = "slab" version = "0.4.6" @@ -1820,15 +1936,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973" -[[package]] -name = "users" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fed7d0912567d35f88010c23dbaf865e9da8b5227295e8dc0f2fdd109155ab7" -dependencies = [ - "libc", -] - [[package]] name = "value-bag" version = "1.0.0-alpha.9" @@ -1963,6 +2070,17 @@ dependencies = [ "libc", ] +[[package]] +name = "which" +version = "4.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c4fb54e6113b6a8772ee41c3404fb0301ac79604489467e0a9ce1f3e97c24ae" +dependencies = [ + "either", + "lazy_static", + "libc", +] + [[package]] name = "winapi" version = "0.3.9" @@ -2048,7 +2166,7 @@ name = "xkbcommon-sys" version = "0.7.5" source = "git+https://github.com/kelnos/rust-xkbcommon-sys?branch=release-0.7.x#8579153d01ef87096abf0ef02608e5dde8e96a0c" dependencies = [ - "bindgen", + "bindgen 0.56.0", "libc", "pkg-config", ] diff --git a/debian/control b/debian/control index ef8918f..9f28e3b 100644 --- a/debian/control +++ b/debian/control @@ -14,6 +14,7 @@ Build-Depends: libpam0g-dev, cargo (>= 0.57), rustc (>= 1.58), + clang, help2man Package: bscreensaver diff --git a/dialog-gtk3/Cargo.toml b/dialog-gtk3/Cargo.toml index 40d820e..5af8866 100644 --- a/dialog-gtk3/Cargo.toml +++ b/dialog-gtk3/Cargo.toml @@ -14,5 +14,5 @@ gtk-sys = "0.15" gdk-sys = "0.15" gdkx11 = "0.15" log = "0.4" -pam = "0.7" +pam-client = "0.5" shell-words = "1" diff --git a/dialog-gtk3/src/main.rs b/dialog-gtk3/src/main.rs index cabac19..f6d564b 100644 --- a/dialog-gtk3/src/main.rs +++ b/dialog-gtk3/src/main.rs @@ -1,16 +1,78 @@ use chrono::prelude::*; use gdkx11::X11Window; use gethostname::gethostname; -use glib::{GString, clone, SourceId}; +use glib::{GString, clone}; use gtk::{prelude::*, Button, Entry, Label, Plug, Window}; use log::{debug, error, warn}; -use std::{io::{self, Write}, thread, time::Duration}; +use std::{io::{self, Write}, sync::mpsc, thread, time::Duration, ffi::CString}; use bscreensaver_util::{init_logging, dialog::DialogExitStatus, settings::Configuration, desktop::NewLoginCommand}; +const PAM_SERVICE_NAME: &str = "xscreensaver"; const DIALOG_UPDATE_INTERVAL: Duration = Duration::from_millis(100); const DIALOG_TIMEOUT: Duration = Duration::from_secs(60); +#[derive(Debug)] +enum GtkConverseOp { + ShowInfo(String), + ShowError(String), + ShowPrompt(String, mpsc::Sender), + ShowBlindPrompt(String, mpsc::Sender), +} + +struct Authenticator { + username: String, + sender: glib::Sender, +} + +impl Authenticator { + pub fn new>(username: S, op_sender: glib::Sender) -> Self { + Self { + username: username.as_ref().to_string(), + sender: op_sender, + } + } + + pub fn authenticate(mut self) -> anyhow::Result<()> { + let username = std::mem::take(&mut self.username); + let mut context = pam_client::Context::new(PAM_SERVICE_NAME, Some(username.as_str()), self)?; + context.authenticate(pam_client::Flag::NONE)?; + context.acct_mgmt(pam_client::Flag::NONE)?; + let _ = context.open_session(pam_client::Flag::NONE)?; + Ok(()) + } +} + +impl pam_client::ConversationHandler for Authenticator { + fn text_info(&mut self, msg: &std::ffi::CStr) { + let msg = msg.to_str().unwrap_or("(OS sent us a non-UTF-8 message)").trim(); + let _ = self.sender.send(GtkConverseOp::ShowInfo(msg.to_string())); + thread::sleep(Duration::from_secs(2)); + } + + fn error_msg(&mut self, msg: &std::ffi::CStr) { + let msg = msg.to_str().unwrap_or("(OS sent us a non-UTF-8 message)").trim(); + let _ = self.sender.send(GtkConverseOp::ShowError(msg.to_string())); + thread::sleep(Duration::from_secs(2)); + } + + fn prompt_echo_on(&mut self, msg: &std::ffi::CStr) -> Result { + let msg = msg.to_str().unwrap_or("Password:").trim(); + let (tx, rx) = mpsc::channel(); + self.sender.send(GtkConverseOp::ShowPrompt(msg.to_string(), tx)).map_err(|_| pam_client::ErrorCode::ABORT)?; + let result = rx.recv().map_err(|_| pam_client::ErrorCode::ABORT)?; + CString::new(result).map_err(|_| pam_client::ErrorCode::CRED_ERR) + } + + fn prompt_echo_off(&mut self, msg: &std::ffi::CStr) -> Result { + let msg = msg.to_str().unwrap_or("Password:").trim(); + let (tx, rx) = mpsc::channel(); + self.sender.send(GtkConverseOp::ShowBlindPrompt(msg.to_string(), tx)).map_err(|_| pam_client::ErrorCode::ABORT)?; + let result = rx.recv().map_err(|_| pam_client::ErrorCode::ABORT)?; + CString::new(result).map_err(|_| pam_client::ErrorCode::CRED_ERR) + } +} + fn main() -> anyhow::Result<()> { init_logging("BSCREENSAVER_DIALOG_GTK3_LOG"); glib::log_set_default_handler(glib::rust_log_handler); @@ -27,8 +89,6 @@ fn main() -> anyhow::Result<()> { gtk::init()?; - let (tx, rx) = glib::MainContext::sync_channel(glib::PRIORITY_DEFAULT, 1); - let top_sg = gtk::SizeGroup::builder() .mode(gtk::SizeGroupMode::Horizontal) .build(); @@ -150,32 +210,6 @@ fn main() -> anyhow::Result<()> { glib::Continue(true) }); - let attrs = gtk::pango::AttrList::new(); - attrs.insert(gtk::pango::AttrFloat::new_scale(gtk::pango::SCALE_XX_LARGE)); - let mut bold_desc = gtk::pango::FontDescription::new(); - bold_desc.set_weight(gtk::pango::Weight::Bold); - attrs.insert(gtk::pango::AttrFontDesc::new(&bold_desc)); - let auth_status_label = gtk::Label::builder() - .label("") - .xalign(0.5) - .yalign(0.5) - .attributes(&attrs) - .opacity(0.0) - .build(); - vbox.pack_start(&auth_status_label, false, false, 0); - - rx.attach(None, clone!(@strong auth_status_label => move |(exit_status, auth_dots_id): (DialogExitStatus, SourceId)| { - auth_dots_id.remove(); - if exit_status != DialogExitStatus::AuthSucceeded { - auth_status_label.set_label("Authentication Failed!"); - auth_status_label.set_opacity(1.0); - glib::timeout_add_seconds_local_once(1, move || exit_status.exit()); - } else { - exit_status.exit(); - } - glib::Continue(true) - })); - let sep = gtk::Separator::builder() .orientation(gtk::Orientation::Vertical) .build(); @@ -183,118 +217,46 @@ fn main() -> anyhow::Result<()> { let vbox = gtk::Box::builder() .orientation(gtk::Orientation::Vertical) - .spacing(4) + .spacing(0) + .margin(0) .build(); top_sg.add_widget(&vbox); top_hbox.pack_start(&vbox, true, true, 0); - let hbox = gtk::Box::builder() + let auth_vbox = gtk::Box::builder() + .orientation(gtk::Orientation::Vertical) + .spacing(4) + .build(); + vbox.pack_start(&auth_vbox, true, true, 2); + + let button_hbox = gtk::Box::builder() .orientation(gtk::Orientation::Horizontal) .spacing(8) .build(); - vbox.pack_start(&hbox, true, true, 2); - - let label = Label::builder() - .label("Username:") - .xalign(0.0) - .build(); - label_sg.add_widget(&label); - hbox.pack_start(&label, false, true, 8); - - let username = bscreensaver_util::get_username()?; - let username_box = Entry::builder() - .text(&username) - .sensitive(false) - .build(); - entry_sg.add_widget(&username_box); - hbox.pack_start(&username_box, true, true, 8); - - let hbox = gtk::Box::builder() - .orientation(gtk::Orientation::Horizontal) - .spacing(8) - .build(); - vbox.pack_start(&hbox, true, true, 0); - - let label = Label::builder() - .label("Password:") - .xalign(0.0) - .build(); - label_sg.add_widget(&label); - hbox.pack_start(&label, false, true, 8); - - let password_box = Entry::builder() - .visibility(false) - .input_purpose(gtk::InputPurpose::Password) - .activates_default(true) - .width_chars(25) - .build(); - entry_sg.add_widget(&password_box); - hbox.pack_start(&password_box, true, true, 8); - password_box.connect_key_press_event(|_, ev| { - if ev.keyval().name() == Some(GString::from("Escape")) { - DialogExitStatus::AuthCanceled.exit(); - } - gtk::Inhibit(false) - }); - password_box.grab_focus(); - - let hbox = gtk::Box::builder() - .orientation(gtk::Orientation::Horizontal) - .spacing(8) - .build(); - vbox.pack_start(&hbox, true, true, 2); + vbox.pack_end(&button_hbox, false, false, 4); if let Some(new_login_command) = new_login_command { - let new_login_button = Button::builder() + let button = Button::builder() .label("New Login") .build(); - new_login_button.connect_clicked(move |_| { - let new_login_command = new_login_command.clone(); - thread::spawn(move || { + button.connect_clicked(clone!(@strong new_login_command => move |_| { + thread::spawn(clone!(@strong new_login_command => move || { if let Err(err) = new_login_command.run() { warn!("Failed to run new login command: {}", err); } else { - DialogExitStatus::OtherError.exit(); + DialogExitStatus::AuthCanceled.exit(); } - }); - }); - hbox.pack_start(&new_login_button, false, true, 8); + })); + })); + button_hbox.pack_start(&button, false, true, 8); } - let unlock_button = Button::builder() - .label("Unlock") + let accept_button_box = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .spacing(0) + .margin(0) .build(); - unlock_button.connect_clicked(clone!(@strong password_box, @strong auth_status_label => move |unlock_button| { - unlock_button.set_sensitive(false); - password_box.set_sensitive(false); - - let username = username.clone(); - let password = password_box.text().to_string(); - - auth_status_label.set_label("Authenticating"); - auth_status_label.set_opacity(1.0); - let auth_dots_id = glib::timeout_add_local(Duration::from_millis(500), clone!(@strong auth_status_label => move || { - let s = auth_status_label.label(); - auth_status_label.set_label(&(s.as_str().to_string() + ".")); - Continue(true) - })); - - let tx = tx.clone(); - thread::spawn(move || { - let status = if authenticate(&username, &password) { - DialogExitStatus::AuthSucceeded - } else { - DialogExitStatus::AuthFailed - }; - if let Err(err) = tx.send((status, auth_dots_id)) { - error!("Failed to send exit status to main thread: {}", err); - DialogExitStatus::OtherError.exit(); - } - }); - })); - hbox.pack_end(&unlock_button, false, true, 8); - unlock_button.set_can_default(true); - unlock_button.set_has_default(true); + button_hbox.pack_end(&accept_button_box, false, true, 8); let timer = gtk::ProgressBar::builder() .orientation(gtk::Orientation::Horizontal) @@ -304,9 +266,8 @@ fn main() -> anyhow::Result<()> { .margin(2) .build(); top_vbox.pack_end(&timer, false, false, 0); - let delta = (DIALOG_UPDATE_INTERVAL.as_millis() as f64) / (DIALOG_TIMEOUT.as_millis() as f64); - glib::timeout_add_local(DIALOG_UPDATE_INTERVAL, clone!(@strong timer => move || { + glib::timeout_add_local(DIALOG_UPDATE_INTERVAL, clone!(@weak timer => @default-return Continue(false), move || { let new_fraction = timer.fraction() - delta; if new_fraction <= 0.0 { DialogExitStatus::AuthTimedOut.exit(); @@ -315,40 +276,136 @@ fn main() -> anyhow::Result<()> { Continue(true) })); - password_box.connect_key_press_event(clone!(@strong timer => move |_, _| { - let new_fraction = timer.fraction() + 0.05; - timer.set_fraction(if new_fraction >= 1.0 { 1.0 } else { new_fraction }); - Inhibit(false) + let (tx, rx) = glib::MainContext::channel(glib::Priority::default()); + let username = bscreensaver_util::get_username()?; + + rx.attach(None, clone!(@weak dialog, @weak auth_vbox, @weak accept_button_box, @weak timer, @strong username, => @default-return glib::Continue(false), move |op| { + for child in auth_vbox.children() { + auth_vbox.remove(&child); + } + for child in accept_button_box.children() { + accept_button_box.remove(&child); + } + + debug!("PAM saysL {:?}", op); + + let input_purpose = match op { + GtkConverseOp::ShowBlindPrompt(_, _) => gtk::InputPurpose::Password, + _ => gtk::InputPurpose::FreeForm, + }; + + match op { + GtkConverseOp::ShowInfo(msg) | GtkConverseOp::ShowError(msg) => { + let attrs = gtk::pango::AttrList::new(); + attrs.insert(gtk::pango::AttrFloat::new_scale(gtk::pango::SCALE_LARGE)); + let label = gtk::Label::builder() + .label(&msg) + .xalign(0.0) + .width_chars(40) + .wrap(true) + .wrap_mode(gtk::pango::WrapMode::Word) + .attributes(&attrs) + .build(); + auth_vbox.pack_start(&label, false, false, 0); + }, + + GtkConverseOp::ShowPrompt(msg, result_sender) | GtkConverseOp::ShowBlindPrompt(msg, result_sender) => { + let hbox = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .spacing(8) + .build(); + auth_vbox.pack_start(&hbox, true, true, 2); + + let label = Label::builder() + .label("Username:") + .xalign(0.0) + .build(); + label_sg.add_widget(&label); + hbox.pack_start(&label, false, true, 8); + + let username_box = Entry::builder() + .text(&username) + .sensitive(false) + .build(); + entry_sg.add_widget(&username_box); + hbox.pack_start(&username_box, true, true, 8); + + let hbox = gtk::Box::builder() + .orientation(gtk::Orientation::Horizontal) + .spacing(8) + .build(); + auth_vbox.pack_start(&hbox, true, true, 0); + + let label = Label::builder() + .label(&msg) + .xalign(0.0) + .build(); + label_sg.add_widget(&label); + hbox.pack_start(&label, false, true, 8); + + let input_box = Entry::builder() + .visibility(false) + .input_purpose(input_purpose) + .activates_default(true) + .width_chars(25) + .build(); + entry_sg.add_widget(&input_box); + hbox.pack_start(&input_box, true, true, 8); + input_box.connect_key_press_event(clone!(@strong timer => move |_, ev| { + let new_fraction = timer.fraction() + 0.05; + timer.set_fraction(if new_fraction >= 1.0 { 1.0 } else { new_fraction }); + + if ev.keyval().name() == Some(GString::from("Escape")) { + DialogExitStatus::AuthCanceled.exit(); + } + gtk::Inhibit(false) + })); + input_box.grab_focus(); + + let accept_button = Button::builder() + .label("Unlock") + .build(); + accept_button.connect_clicked(clone!(@strong input_box => move |accept_button| { + accept_button.set_sensitive(false); + input_box.set_sensitive(false); + + if let Err(err) = result_sender.send(input_box.text().to_string()) { + warn!("Failed to send result to main thread: {}", err); + } + })); + accept_button_box.pack_start(&accept_button, true, true, 0); + accept_button.set_can_default(true); + accept_button.set_has_default(true); + } + }; + + dialog.show_all(); + glib::Continue(true) })); - dialog.show_all(); + thread::spawn(move || { + let authenticator = Authenticator::new(&username, tx.clone()); + if let Err(err) = authenticator.authenticate() { + error!("PAM: {}", err); + if tx.send(GtkConverseOp::ShowError("Authentication Failed".to_string())).is_ok() { + thread::sleep(Duration::from_secs(2)); + } + DialogExitStatus::AuthFailed.exit(); + } else { + debug!("PAM success"); + DialogExitStatus::AuthSucceeded.exit(); + } + }); gtk::main(); - DialogExitStatus::OtherError.exit(); // We should never get here! + + // We never call gtk::main_quit(), so we should never get here. If + // we do, it'd be a bad bug, because we'd exit with status 0, which + // would be interpreted as "auth success". + DialogExitStatus::OtherError.exit(); } fn set_time_label(label: >k::Label) { let now = Local::now().format("%Y-%m-%d %H:%M:%S").to_string(); label.set_label(&now); } - -fn authenticate(username: &String, password: &String) -> bool { - let mut authenticator = match pam::Authenticator::with_password("xscreensaver") { - Err(err) => { - error!("[PAM] {}", err); - return false; - }, - Ok(authenticator) => authenticator, - }; - authenticator.get_handler().set_credentials(username, password); - if let Err(err) = authenticator.authenticate() { - error!("[PAM] {}", err); - return false; - } - if let Err(err) = authenticator.open_session() { - error!("[PAM] {}", err); - false - } else { - true - } -}