1616use std:: ffi:: c_void;
1717use std:: io:: { self , ErrorKind , Read } ;
1818use std:: os:: fd:: { AsFd , AsRawFd , BorrowedFd } ;
19+ use std:: path:: PathBuf ;
1920use std:: time:: { Duration , Instant } ;
2021use std:: { fs, mem} ;
2122
2223use libc:: { tcsetattr, termios, ECHO , ECHONL , ICANON , TCSANOW , VEOF , VERASE , VKILL } ;
2324
2425use crate :: cutils:: { cerr, safe_isatty} ;
25- use crate :: pam:: { PamError , PamResult } ;
26+ use crate :: pam:: { askpass, PamError , PamResult } ;
27+ use crate :: system:: wait:: { Wait , WaitError , WaitOptions } ;
2628
2729use super :: securemem:: PamBuffer ;
2830
@@ -253,6 +255,7 @@ impl io::Read for TimeoutRead<'_> {
253255pub enum Terminal < ' a > {
254256 Tty ( fs:: File ) ,
255257 StdIE ( io:: StdinLock < ' a > , io:: StderrLock < ' a > ) ,
258+ Askpass ( PathBuf , io:: Sink ) ,
256259}
257260
258261impl Terminal < ' _ > {
@@ -274,6 +277,19 @@ impl Terminal<'_> {
274277 Ok ( Terminal :: StdIE ( io:: stdin ( ) . lock ( ) , io:: stderr ( ) . lock ( ) ) )
275278 }
276279
280+ pub fn open_askpass ( ) -> PamResult < Self > {
281+ let Some ( program) = std:: env:: var_os ( "SUDO_ASKPASS" ) else {
282+ return Err ( PamError :: NoAskpassProgram ) ;
283+ } ;
284+ let program = PathBuf :: from ( program) ;
285+
286+ if program. is_absolute ( ) {
287+ Ok ( Terminal :: Askpass ( program, io:: sink ( ) ) )
288+ } else {
289+ Err ( PamError :: InvalidAskpassProgram ( program) )
290+ }
291+ }
292+
277293 /// Reads input with TTY echo and visual feedback set according to the `hidden` parameter.
278294 pub ( super ) fn read_input (
279295 & mut self ,
@@ -310,6 +326,23 @@ impl Terminal<'_> {
310326 let mut reader = TimeoutRead :: new ( file. as_fd ( ) , timeout) ;
311327 read_unbuffered ( & mut reader, & mut & * file, & hide_input)
312328 }
329+ Terminal :: Askpass ( program, sink) => {
330+ let ( command_pid, askpass_stdout) = askpass:: spawn_askpass ( program, prompt) ?;
331+
332+ let mut reader = TimeoutRead :: new ( askpass_stdout. as_fd ( ) , timeout) ;
333+ let password = read_unbuffered ( & mut reader, sink, & Hidden :: No ) ?;
334+
335+ loop {
336+ match command_pid. wait ( WaitOptions :: new ( ) ) {
337+ Ok ( _) => break ,
338+ Err ( WaitError :: Io ( err) ) if err. kind ( ) == io:: ErrorKind :: Interrupted => { }
339+ Err ( WaitError :: Io ( err) ) => return Err ( PamError :: IoError ( err) ) ,
340+ Err ( WaitError :: NotReady ) => unreachable ! ( ) ,
341+ }
342+ }
343+
344+ Ok ( password)
345+ }
313346 }
314347 }
315348
@@ -329,6 +362,7 @@ impl Terminal<'_> {
329362 match self {
330363 Terminal :: StdIE ( _, x) => x,
331364 Terminal :: Tty ( x) => x,
365+ Terminal :: Askpass ( _, x) => x,
332366 }
333367 }
334368}
0 commit comments