Skip to content

Commit e8043a7

Browse files
authored
Mask SIG{INT,QUIT} during pam_authenticate call (#1322)
2 parents b5bbd74 + 534e287 commit e8043a7

File tree

3 files changed

+74
-20
lines changed

3 files changed

+74
-20
lines changed

src/pam/converse.rs

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use std::io;
22

33
use crate::cutils::string_from_ptr;
4-
use crate::system::time::Duration;
4+
use crate::system::{
5+
signal::{self, SignalSet},
6+
time::Duration,
7+
};
58

69
use super::sys::*;
710

@@ -65,16 +68,10 @@ fn handle_message<C: Converser>(
6568
use PamMessageStyle::*;
6669

6770
match style {
68-
PromptEchoOn => {
69-
if app_data.no_interact {
70-
return Err(PamError::InteractionRequired);
71-
}
72-
app_data.converser.handle_normal_prompt(msg).map(Some)
73-
}
71+
PromptEchoOn | PromptEchoOff if app_data.no_interact => Err(PamError::InteractionRequired),
72+
73+
PromptEchoOn => app_data.converser.handle_normal_prompt(msg).map(Some),
7474
PromptEchoOff => {
75-
if app_data.no_interact {
76-
return Err(PamError::InteractionRequired);
77-
}
7875
let final_prompt = match app_data.auth_prompt.as_deref() {
7976
None => {
8077
// Suppress password prompt entirely when -p '' is passed.
@@ -89,6 +86,7 @@ fn handle_message<C: Converser>(
8986
.handle_hidden_prompt(&final_prompt)
9087
.map(Some)
9188
}
89+
9290
ErrorMessage => app_data.converser.handle_error(msg).map(|()| None),
9391
TextInfo => app_data.converser.handle_info(msg).map(|()| None),
9492
}
@@ -106,25 +104,50 @@ pub struct CLIConverser {
106104

107105
use rpassword::Terminal;
108106

107+
struct SignalGuard(Option<SignalSet>);
108+
109+
impl SignalGuard {
110+
fn unblock_interrupts() -> Self {
111+
let cur_signals = SignalSet::empty().and_then(|mut set| {
112+
set.add(signal::consts::SIGINT)?;
113+
set.add(signal::consts::SIGQUIT)?;
114+
set.unblock()
115+
});
116+
117+
Self(cur_signals.ok())
118+
}
119+
}
120+
121+
impl Drop for SignalGuard {
122+
fn drop(&mut self) {
123+
if let Some(signal) = &self.0 {
124+
// Ignore any errors at this point
125+
let _ = signal.set_mask();
126+
}
127+
}
128+
}
129+
109130
impl CLIConverser {
110-
fn open(&self) -> std::io::Result<Terminal<'_>> {
111-
if self.use_stdin {
112-
Terminal::open_stdie()
131+
fn open(&self) -> std::io::Result<(Terminal<'_>, SignalGuard)> {
132+
let term = if self.use_stdin {
133+
Terminal::open_stdie()?
113134
} else {
114-
Terminal::open_tty()
115-
}
135+
Terminal::open_tty()?
136+
};
137+
138+
Ok((term, SignalGuard::unblock_interrupts()))
116139
}
117140
}
118141

119142
impl Converser for CLIConverser {
120143
fn handle_normal_prompt(&self, msg: &str) -> PamResult<PamBuffer> {
121-
let mut tty = self.open()?;
144+
let (mut tty, _guard) = self.open()?;
122145
tty.prompt(&format!("[{}: input needed] {msg} ", self.name))?;
123146
Ok(tty.read_cleartext()?)
124147
}
125148

126149
fn handle_hidden_prompt(&self, msg: &str) -> PamResult<PamBuffer> {
127-
let mut tty = self.open()?;
150+
let (mut tty, _guard) = self.open()?;
128151
if self.bell && !self.use_stdin {
129152
tty.bell()?;
130153
}
@@ -144,12 +167,12 @@ impl Converser for CLIConverser {
144167
}
145168

146169
fn handle_error(&self, msg: &str) -> PamResult<()> {
147-
let mut tty = self.open()?;
170+
let (mut tty, _) = self.open()?;
148171
Ok(tty.prompt(&format!("[{} error] {msg}\n", self.name))?)
149172
}
150173

151174
fn handle_info(&self, msg: &str) -> PamResult<()> {
152-
let mut tty = self.open()?;
175+
let (mut tty, _) = self.open()?;
153176
Ok(tty.prompt(&format!("[{}] {msg}\n", self.name))?)
154177
}
155178
}

src/pam/mod.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use std::{
66
ptr::NonNull,
77
};
88

9-
use crate::system::time::Duration;
9+
use crate::system::{
10+
signal::{self, SignalSet},
11+
time::Duration,
12+
};
1013

1114
use converse::ConverserData;
1215
use error::pam_err;
@@ -153,9 +156,21 @@ impl PamContext {
153156
flags |= self.silent_flag();
154157
flags |= self.disallow_null_auth_token_flag();
155158

159+
// Temporarily mask SIGINT and SIGQUIT.
160+
let cur_signals = SignalSet::empty().and_then(|mut set| {
161+
set.add(signal::consts::SIGINT)?;
162+
set.add(signal::consts::SIGQUIT)?;
163+
set.block()
164+
});
165+
156166
// SAFETY: `self.pamh` contains a correct handle (obtained from `pam_start`)
157167
let auth_res = pam_err(unsafe { pam_authenticate(self.pamh, flags) });
158168

169+
// Restore signals
170+
if let Ok(set) = cur_signals {
171+
set.set_mask().map_err(PamError::IoError)?;
172+
}
173+
159174
if self.has_panicked() {
160175
panic!("Panic during pam authentication");
161176
}

src/system/signal/set.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,14 @@ impl SignalSet {
7979
Ok(unsafe { set.assume_init() })
8080
}
8181

82+
/// Add a signal to this set
83+
pub(crate) fn add(&mut self, sig: SignalNumber) -> io::Result<()> {
84+
// SAFETY: we pass a valid mutable pointer to `sigaddset`
85+
cerr(unsafe { libc::sigaddset(&mut self.raw, sig) })?;
86+
87+
Ok(())
88+
}
89+
8290
fn sigprocmask(&self, how: libc::c_int) -> io::Result<Self> {
8391
let mut original_set = MaybeUninit::<Self>::zeroed();
8492

@@ -97,6 +105,14 @@ impl SignalSet {
97105
self.sigprocmask(libc::SIG_BLOCK)
98106
}
99107

108+
/// Unblock all the signals in this set and return the previous set of blocked signals.
109+
///
110+
/// After calling this function successfully, the set of blocked signals will be the previous
111+
/// set of blocked signals without this set.
112+
pub(crate) fn unblock(&self) -> io::Result<Self> {
113+
self.sigprocmask(libc::SIG_UNBLOCK)
114+
}
115+
100116
/// Block only the signals that are in this set and return the previous set of blocked signals.
101117
///
102118
/// After calling this function successfully, the set of blocked signals will be the exactly

0 commit comments

Comments
 (0)