diff --git a/include/readpassphrase.h b/include/readpassphrase.h --- a/include/readpassphrase.h +++ b/include/readpassphrase.h @@ -34,6 +34,8 @@ #define RPP_SEVENBIT 0x10 /* Strip the high bit from input. */ #define RPP_STDIN 0x20 /* Read from stdin, not /dev/tty */ +#define AT_STDIN -101 /* File descriptor equivalent of RPP_STDIN. */ + #include #include @@ -44,6 +46,7 @@ __BEGIN_DECLS char * readpassphrase(const char *, char *, size_t, int); +char * readpassphraseat(int, const char *, char *, size_t, int); __END_DECLS #endif /* !_READPASSPHRASE_H_ */ diff --git a/lib/libc/gen/Makefile.inc b/lib/libc/gen/Makefile.inc --- a/lib/libc/gen/Makefile.inc +++ b/lib/libc/gen/Makefile.inc @@ -494,6 +494,7 @@ rand48.3 nrand48.3 \ rand48.3 seed48.3 \ rand48.3 srand48.3 +MLINKS+=readpassphrase.3 readpassphraseat.3 MLINKS+=recv.2 recvmmsg.2 MLINKS+=scandir.3 alphasort.3 \ scandir.3 scandirat.3 \ diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map --- a/lib/libc/gen/Symbol.map +++ b/lib/libc/gen/Symbol.map @@ -439,6 +439,7 @@ posix_spawn_file_actions_addchdir_np; posix_spawn_file_actions_addclosefrom_np; posix_spawn_file_actions_addfchdir_np; + readpassphraseat; scandirat; sched_getaffinity; sched_setaffinity; diff --git a/lib/libc/gen/readpassphrase.3 b/lib/libc/gen/readpassphrase.3 --- a/lib/libc/gen/readpassphrase.3 +++ b/lib/libc/gen/readpassphrase.3 @@ -30,6 +30,8 @@ .In readpassphrase.h .Ft "char *" .Fn readpassphrase "const char *prompt" "char *buf" "size_t bufsiz" "int flags" +.Ft "char *" +.Fn readpassphraseat "int ttyfd" "const char *prompt" "char *buf" "size_t bufsiz" "int flags" .Sh DESCRIPTION The .Fn readpassphrase @@ -54,9 +56,26 @@ characters and the terminating newline (or return) character are discarded. .Pp The +.Fn readpassphraseat +function is equivalent to +.Fn readpassphrase , +but allows the user to specify a +.Fa ttyfd +terminal type device for prompting and password retrieval. +Programs in +.Xr capsicum 4 +capability mode can pass a pre-opened +.Pa /dev/tty +file descriptor into +.Fn readpassphraseat +to avoid triggering a capability violation. +.Pp +The .Fn readpassphrase -function -takes the following optional +and +.Fn readpassphraseat +functions +take the following optional .Fa flags : .Pp .Bl -tag -width ".Dv RPP_REQUIRE_TTY" -compact @@ -76,13 +95,24 @@ force read of passphrase from stdin .El .Pp +If +.Dv AT_STDIN +is assigned to +.Fa ttyfd , +the passphrase will be force read from stdin. +This is equivalent to specifying the +.Dv RPP_STDIN +flag. +.Pp The calling process should zero the passphrase as soon as possible to avoid leaving the cleartext passphrase visible in the process's address space. .Sh RETURN VALUES Upon successful completion, .Fn readpassphrase -returns a pointer to the NUL-terminated passphrase. +and +.Fn readpassphraseat +return a pointer to the NUL-terminated passphrase. If an error is encountered, the terminal state is restored and a .Dv NULL @@ -115,9 +145,7 @@ .Sh ERRORS .Bl -tag -width Er .It Bq Er EINTR -The -.Fn readpassphrase -function was interrupted by a signal. +The passphrase reading was interrupted by a signal. .It Bq Er EINVAL The .Ar bufsiz @@ -136,6 +164,14 @@ There is no controlling terminal and the .Dv RPP_REQUIRE_TTY flag was specified. +This error is only applicable to +.Fn readpassphrase . +.It Bq Er ENOTTY +The provided +.Fa ttyfd +is an invalid controlling terminal. +This error is only applicable to +.Fn readpassphraseat . .El .Sh SIGNALS The @@ -168,7 +204,8 @@ will reprint the prompt and the user may then enter a passphrase. .Sh SEE ALSO .Xr sigaction 2 , -.Xr getpass 3 +.Xr getpass 3 , +.Xr capsicum 4 .Sh STANDARDS The .Fn readpassphrase diff --git a/lib/libc/gen/readpassphrase.c b/lib/libc/gen/readpassphrase.c --- a/lib/libc/gen/readpassphrase.c +++ b/lib/libc/gen/readpassphrase.c @@ -43,7 +43,7 @@ static void handler(int); char * -readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) +readpassphraseat(int ttyfd, const char *prompt, char *buf, size_t bufsiz, int flags) { ssize_t nr; int input, output, save_errno, i, need_restart, input_is_tty; @@ -58,6 +58,13 @@ return(NULL); } + /* Passing AT_STDIN in ttyfd is identical to RPP_STDIN. */ + if (ttyfd == AT_STDIN || + ttyfd == STDIN_FILENO || + ttyfd == STDOUT_FILENO || + ttyfd == STDERR_FILENO) + flags |= RPP_STDIN; + restart: for (i = 0; i < NSIG; i++) signo[i] = 0; @@ -65,22 +72,17 @@ save_errno = 0; need_restart = 0; /* - * Read and write to /dev/tty if available. If not, read from - * stdin and write to stderr unless a tty is required. + * Read and write to terminal type device if valid. If not, read + * from stdin and write to stderr. */ input_is_tty = 0; - if (!(flags & RPP_STDIN)) { - input = output = _open(_PATH_TTY, O_RDWR | O_CLOEXEC); - if (input == -1) { - if (flags & RPP_REQUIRE_TTY) { - errno = ENOTTY; - return(NULL); - } - input = STDIN_FILENO; - output = STDERR_FILENO; - } else { - input_is_tty = 1; + if ((flags & RPP_STDIN) == 0) { + if (isatty(ttyfd) != 1) { + errno = ENOTTY; + return (NULL); } + input_is_tty = 1; + input = output = ttyfd; } else { input = STDIN_FILENO; output = STDERR_FILENO; @@ -160,8 +162,6 @@ (void)__libc_sigaction(SIGTSTP, &savetstp, NULL); (void)__libc_sigaction(SIGTTIN, &savettin, NULL); (void)__libc_sigaction(SIGTTOU, &savettou, NULL); - if (input_is_tty) - (void)_close(input); /* * If we were interrupted by a signal, resend it to ourselves @@ -186,6 +186,37 @@ return(nr == -1 ? NULL : buf); } +char * +readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags) +{ + int save_errno, ttyfd; + char *error; + + ttyfd = AT_STDIN; + /* + * Pass /dev/tty fd into readpassphraseat() if available. + * Otherwise, resort to stdin unless a tty is required. + */ + if ((flags & RPP_STDIN) == 0) { + ttyfd = _open(_PATH_TTY, O_RDWR | O_CLOEXEC); + if (ttyfd == -1) { + if ((flags & RPP_REQUIRE_TTY) != 0) { + errno = ENOTTY; + return (NULL); + } + ttyfd = AT_STDIN; + } + } + error = readpassphraseat(ttyfd, prompt, buf, bufsiz, flags); + + save_errno = errno; + if (ttyfd != AT_STDIN) + (void)_close(ttyfd); + errno = save_errno; + + return (error); +} + char * getpass(const char *prompt) {