11/*
22 * SPDX-License-Identifier: ISC
33 *
4- * Copyright (c) 1996, 1998-2005, 2007-2021
4+ * Copyright (c) 1996, 1998-2005, 2007-2025
55 * Todd C. Miller <[email protected] > 66 *
77 * Permission to use, copy, modify, and distribute this software for any
@@ -49,7 +49,7 @@ enum tgetpass_errval {
4949static volatile sig_atomic_t signo [NSIG ];
5050
5151static void tgetpass_handler (int );
52- static char * getln (int , char * , size_t , bool , enum tgetpass_errval * );
52+ static char * getln (int , char * , size_t , bool , bool , enum tgetpass_errval * );
5353static char * sudo_askpass (const char * , const char * );
5454
5555static int
@@ -112,7 +112,7 @@ tgetpass(const char *prompt, int timeout, unsigned int flags,
112112 static const char * askpass ;
113113 static char buf [SUDO_CONV_REPL_MAX + 1 ];
114114 int i , input , output , save_errno , ttyfd ;
115- bool feedback , need_restart , neednl ;
115+ bool cbreak , feedback , need_restart ;
116116 enum tgetpass_errval errval ;
117117 debug_decl (tgetpass , SUDO_DEBUG_CONV );
118118
@@ -158,7 +158,7 @@ tgetpass(const char *prompt, int timeout, unsigned int flags,
158158 signo [i ] = 0 ;
159159 pass = NULL ;
160160 save_errno = 0 ;
161- neednl = false;
161+ cbreak = false;
162162 need_restart = false;
163163 feedback = false;
164164
@@ -173,17 +173,21 @@ tgetpass(const char *prompt, int timeout, unsigned int flags,
173173 output = ttyfd ;
174174 }
175175
176- /*
177- * If we are using a tty but are not the foreground pgrp this will
178- * return EINTR. We send ourself SIGTTOU bracketed by callbacks.
179- */
180176 if (!ISSET (flags , TGP_ECHO )) {
177+ /*
178+ * Instead of just disabling echo, we enable "cbreak" mode.
179+ * This lets us read one character at a time, which is also
180+ * necessary when masking input (for the pwfeedback option).
181+ */
181182 for (;;) {
183+ /*
184+ * If we are using a tty but are not the foreground pgrp this will
185+ * return EINTR. We send ourself SIGTTOU bracketed by callbacks.
186+ */
187+ cbreak = sudo_term_cbreak (input , true);
182188 if (ISSET (flags , TGP_MASK ))
183- neednl = feedback = sudo_term_cbreak (input , true);
184- else
185- neednl = sudo_term_noecho (input );
186- if (neednl || errno != EINTR )
189+ feedback = cbreak ;
190+ if (cbreak || errno != EINTR )
187191 break ;
188192 /* Received SIGTTOU, suspend the process. */
189193 if (suspend (SIGTTOU , callback ) == -1 ) {
@@ -216,18 +220,19 @@ tgetpass(const char *prompt, int timeout, unsigned int flags,
216220 if (write (output , "\a" , 1 ) != 1 )
217221 goto restore ;
218222 }
219- if (prompt ) {
223+ if (prompt != NULL ) {
220224 if (write (output , prompt , strlen (prompt )) < 0 )
221225 goto restore ;
222226 }
223227
224228 if (timeout > 0 )
225229 alarm ((unsigned int )timeout );
226- pass = getln (input , buf , sizeof (buf ), feedback , & errval );
230+ pass = getln (input , buf , sizeof (buf ), cbreak , feedback , & errval );
227231 alarm (0 );
228232 save_errno = errno ;
229233
230- if (neednl || pass == NULL ) {
234+ if (cbreak || pass == NULL ) {
235+ /* No newline was displayed. */
231236 if (write (output , "\n" , 1 ) != 1 )
232237 goto restore ;
233238 }
@@ -345,7 +350,7 @@ sudo_askpass(const char *askpass, const char *prompt)
345350
346351 /* Get response from child (askpass). */
347352 (void ) close (pfd [1 ]);
348- pass = getln (pfd [0 ], buf , sizeof (buf ), 0 , & errval );
353+ pass = getln (pfd [0 ], buf , sizeof (buf ), false, false , & errval );
349354 (void ) close (pfd [0 ]);
350355
351356 tgetpass_display_error (errval );
@@ -392,7 +397,7 @@ last_chunk_len(const char *buf, ssize_t len)
392397}
393398
394399static char *
395- getln (int fd , char * buf , size_t bufsiz , bool feedback ,
400+ getln (int fd , char * buf , size_t bufsiz , bool cbreak , bool feedback ,
396401 enum tgetpass_errval * errval )
397402{
398403 ssize_t nr = -1 ;
@@ -414,28 +419,33 @@ getln(int fd, char *buf, size_t bufsiz, bool feedback,
414419 nr = read (fd , & c , 1 );
415420 if (nr != 1 || c == '\n' || c == '\r' )
416421 break ;
417- if (feedback ) {
422+ if (cbreak ) {
418423 if (c == sudo_term_eof ) {
419424 nr = 0 ;
420425 break ;
421426 } else if (c == sudo_term_kill ) {
422427 while (cp > buf ) {
423- if (write (fd , "\b \b" , 3 ) != 3 )
424- break ;
428+ if (feedback ) {
429+ if (write (fd , "\b \b" , 3 ) != 3 )
430+ break ;
431+ }
425432 cp -= last_chunk_len (buf , cp - buf );
426433 }
427434 cp = buf ;
428435 continue ;
429436 } else if (c == sudo_term_erase ) {
430437 if (cp > buf ) {
431- ignore_result (write (fd , "\b \b" , 3 ));
438+ if (feedback )
439+ ignore_result (write (fd , "\b \b" , 3 ));
432440 cp -= last_chunk_len (buf , cp - buf );
433441 }
434442 continue ;
435443 }
436444 * cp ++ = c ;
437- if (last_chunk_len (buf , cp - buf ) == 1 )
438- ignore_result (write (fd , "*" , 1 ));
445+ if (feedback ) {
446+ if (last_chunk_len (buf , cp - buf ) == 1 )
447+ ignore_result (write (fd , "*" , 1 ));
448+ }
439449 } else {
440450 * cp ++ = c ;
441451 }
0 commit comments