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.
This commit is contained in:
parent
7d05bcb0d5
commit
49394b3e53
160
Cargo.lock
generated
160
Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
1
debian/control
vendored
1
debian/control
vendored
@ -14,6 +14,7 @@ Build-Depends:
|
||||
libpam0g-dev,
|
||||
cargo (>= 0.57),
|
||||
rustc (>= 1.58),
|
||||
clang,
|
||||
help2man
|
||||
|
||||
Package: bscreensaver
|
||||
|
@ -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"
|
||||
|
@ -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<String>),
|
||||
ShowBlindPrompt(String, mpsc::Sender<String>),
|
||||
}
|
||||
|
||||
struct Authenticator {
|
||||
username: String,
|
||||
sender: glib::Sender<GtkConverseOp>,
|
||||
}
|
||||
|
||||
impl Authenticator {
|
||||
pub fn new<S: AsRef<str>>(username: S, op_sender: glib::Sender<GtkConverseOp>) -> 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<CString, pam_client::ErrorCode> {
|
||||
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<CString, pam_client::ErrorCode> {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user