peek(1) #93
82
docs/peek.1
@ -12,52 +12,17 @@ peek \(en read from standard input, furtively
|
||||
.SH SYNOPSIS
|
||||
|
||||
peek
|
||||
.RB ( -1enot )
|
||||
.RB ( -p
|
||||
.RB [ program
|
||||
.RB [ arguments... ]])
|
||||
.RB ( -i )
|
||||
|
||||
.SH DESCRIPTION
|
||||
|
||||
Peek reads input from standard input with terminal echo disabled, which may be
|
||||
useful to prevent secrets being spied upon by adversaries watching a user's
|
||||
screen.
|
||||
Peek reads input from standard input with terminal echo disabled.
|
||||
|
||||
.SH OPTIONS
|
||||
|
||||
emma marked this conversation as resolved
Outdated
|
||||
.B -1
|
||||
.B -i
|
||||
trinity marked this conversation as resolved
Outdated
silt
commented
This appears to imply that there exists a multitude of other possible sources, but it's not made clear what those are or how to make use of them. Does it support reading input from… files? TCP sockets? IRC messages?? This appears to imply that there exists a multitude of other possible sources, but it's not made clear what those are or how to make use of them. Does it support reading input from… files? TCP sockets? IRC messages??
trinity
commented
The "i.e." stands for "id est" and can be read as "in other words". Perhaps I should use the English. I mean to say that the source other than a terminal, in this case, is a pipe. The "i.e." stands for "id est" and can be read as "in other words". Perhaps I should use the English. I mean to say that the source other than a terminal, in this case, is a pipe.
trinity
commented
Changed to (pipes) which I think is more clear. Changed to (pipes) which I think is more clear.
|
||||
.RS
|
||||
Limits input to a single line (stopping when the newline character is read).
|
||||
.RE
|
||||
|
||||
.B -e
|
||||
.RS
|
||||
Configures peek to output to standard error.
|
||||
.RE
|
||||
|
||||
.B -n
|
||||
.RS
|
||||
Prints ("iNcludes") the terminating character in outputs. This is nonsensical
|
||||
if not combined with the
|
||||
.B -1
|
||||
option.
|
||||
.RE
|
||||
|
||||
.B -o
|
||||
.RS
|
||||
Configures peek to output to standard output.
|
||||
.RE
|
||||
|
||||
.B -p
|
||||
.RS
|
||||
Configures peek to pipe output to an executed child program, e.g. a password
|
||||
hashing utility. This is provided as a convenience and to avoid potential
|
||||
insecurities resulting from programmer error in scripts.
|
||||
.RE
|
||||
|
||||
.B -t
|
||||
.RS
|
||||
Makes peek exit if not run within a terminal.
|
||||
Allows input to come from sources other than terminals (i.e. a pipe).
|
||||
.RE
|
||||
|
||||
.SH DIAGNOSTICS
|
||||
@ -70,36 +35,45 @@ still usable after premature termination; if the signal can't be handled, it
|
||||
prints an error message and continues. If peek is interrupted, it exits
|
||||
unsuccessfully, without an error message.
|
||||
|
||||
.SH RATIONALE
|
||||
|
||||
This tool was originally written to accept passwords in shell scripts, as an
|
||||
extremely simple alternative to the GNU Privacy Guard project's pinentry(1).
|
||||
Accepting input without showing what is being typed is useful when keying in
|
||||
secrets in public settings or places with installed surveillance cameras.
|
||||
|
||||
.SH BUGS
|
||||
|
||||
Accepting secrets in shell scripts is not adviseable in any context.
|
||||
This does nothing to prevent others seeing the keyboard being used to input
|
||||
emma marked this conversation as resolved
Outdated
emma
commented
This being in the BUGS section implies that this can be fixed by the program. Therefore, this belongs in a CAVEATS section because it is definitionally a caveat. This being in the BUGS section implies that this can be fixed by the program. Therefore, this belongs in a CAVEATS section because it is definitionally a caveat.
|
||||
secrets or mask the sound of typing. Audio or video recordings of typing can be
|
||||
trinity marked this conversation as resolved
Outdated
emma
commented
“[...] the standard output” “[...] the standard output”
|
||||
used to determine what was input without needing to see the characters appear
|
||||
on the screen.
|
||||
|
||||
trinity marked this conversation as resolved
Outdated
emma
commented
What is the benefit to this over using a shell pipe? What is the benefit to this over using a shell pipe?
|
||||
If used in a safety-critical application it must be ensured that the
|
||||
environment with which peek is used is not compromised.
|
||||
Accepting secrets in shell scripts is probably not adviseable.
|
||||
|
||||
On systems that support it, the ioctl(2) command TIOCSTI can be used to insert
|
||||
characters into the standard input going to peek. This doesn't allow snooping
|
||||
but can be used for general mischief.
|
||||
|
||||
Peek will happily run without outputs, slurping input and doing nothing with
|
||||
it.
|
||||
|
||||
.SH EXAMPLES
|
||||
|
||||
This is a command line for POSIX sh(1), using POSIX env(1) to discard the
|
||||
environment except for PATH, and htpassword(1) from Apache's utilities to hash
|
||||
the input with the bcrypt algorithm, printing the resulting hash:
|
||||
This is an sh(1p) command line that hashes a given password. It uses head(1p)
|
||||
to only accept one line of input, xargs(1p) and printf(1p) to strip the
|
||||
trailing newline, htpasswd(1) from Apache's utilities to hash the input with
|
||||
the bcrypt algorithm, and cut(1p) to print only the resulting hash:
|
||||
|
||||
.RS
|
||||
.R $ env -i PATH="$PATH" peek -1tp htpasswd -nBi _ | cut -d : -f 2
|
||||
.R $ peek | head -n 1 | xargs printf '%s' | htpasswd -nBi _ | cut -d : -f 2
|
||||
.RE
|
||||
|
||||
This is a POSIX sh(1) command line that lets a user blindly write into a text
|
||||
file, only able to see written lines. Some writers have the habit of
|
||||
prematurely revising their work and use tools like this to prevent it:
|
||||
This is an sh(1p) command line that lets a user blindly write into a text file,
|
||||
only able to see written lines. Some writers have the habit of prematurely
|
||||
revising their work and use tools like this to prevent it. It uses mm(1) to
|
||||
pipe the output of peek to both the standard error and the regular file
|
||||
emma marked this conversation as resolved
Outdated
emma
commented
> It uses mm(1) to pipe the hidden input to both the standard error and the regular file writing.txt.
|
||||
writing.txt.
|
||||
|
||||
.RS
|
||||
.R $ echo Input ^D to quit. && peek -eot >writing.txt
|
||||
.R $ echo Input ^D to quit. && peek | mm -eo - >writing.txt
|
||||
.RE
|
||||
|
||||
.SH AUTHOR
|
||||
@ -113,4 +87,4 @@ Copyright (c) 2023-2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
|
||||
|
||||
.SH SEE ALSO
|
||||
|
||||
env(1), ioctl(2), ioctl_tty(2), read(1), sh(1)
|
||||
ioctl(2), ioctl_tty(2), read(1), sh(1)
|
||||
|
72
src/peek.c
@ -17,75 +17,66 @@
|
||||
*/
|
||||
|
||||
#include <signal.h> /* sigaction(2), signal(2), struct sigaction, SIGINT */
|
||||
#include <stdio.h> /* fclose(3), fdopen(3), fprintf(3), getc(3), perror(3),
|
||||
* putc(3), stderr, stdin, stdout, EOF, NULL */
|
||||
#include <stdlib.h> /* exit(3), size_t, EXIT_FAILURE */
|
||||
#include <string.h> /* strerror(3) */
|
||||
#include <stdio.h> /* fprintf(3), fgetc(3), perror(3), fputc(3), stderr, stdin,
|
||||
* stdout, EOF, NULL */
|
||||
#include <stdlib.h> /* exit(3), EXIT_FAILURE */
|
||||
#if !defined EX_OK || !defined EX_OSERR || !defined EX_USAGE
|
||||
# include <sysexits.h>
|
||||
#endif
|
||||
#include <termios.h> /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */
|
||||
#include <unistd.h> /* getopt(3), isatty(3), STDIN_FILENO */
|
||||
|
||||
static int oserr(char *s){ perror(s); return EX_OSERR; }
|
||||
|
||||
static char *program_name = "peek";
|
||||
|
||||
static int usage(char *s){
|
||||
fprintf(stderr, "Usage: %s (-1enot) (-p [program [arguments...]])\n",
|
||||
s == NULL ? program_name : s);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
/* Restores terminal echo; otherwise when a user ^Cs the terminal would
|
||||
* continue to not display typed text. If sig isn't zero, this will terminate
|
||||
* the program. */
|
||||
void restore_echo(int sig){
|
||||
static void restore_echo(int sig){
|
||||
static struct termios t;
|
||||
|
||||
tcgetattr(STDIN_FILENO, &t);
|
||||
t.c_lflag |= ECHO;
|
||||
tcsetattr(STDIN_FILENO, TCSAFLUSH, &t);
|
||||
|
||||
/* If, for whatever ungodly reason, exit(3) returns, the user will notice
|
||||
* their typed characters on the screen. */
|
||||
if(sig != 0)
|
||||
exit(EXIT_FAILURE);
|
||||
else
|
||||
return;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
static int oserr(char *s){ perror(s); return EX_OSERR; }
|
||||
static int ioerr(char *s){ perror(s); restore_echo(0); return EX_IOERR; }
|
||||
static int usage(char *s){
|
||||
fprintf(stderr, "Usage: %s (-1enot) (-p [program [arguments...]])\n", s);
|
||||
return EX_USAGE;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]){
|
||||
int eof;
|
||||
char include_eof;
|
||||
FILE *outputs[] = {
|
||||
NULL /* stdout */,
|
||||
NULL /* stderr */
|
||||
};
|
||||
|
||||
if(argc < 1)
|
||||
return usage(argv[0]);
|
||||
return usage(program_name);
|
||||
|
||||
eof = EOF;
|
||||
include_eof = 0;
|
||||
{ /* options parsing */
|
||||
char allow_nonterminals;
|
||||
int c;
|
||||
|
||||
while((c = getopt(argc, argv, "1enot")) != -1)
|
||||
allow_nonterminals = 0;
|
||||
while((c = getopt(argc, argv, "i")) != -1)
|
||||
switch(c){
|
||||
trinity marked this conversation as resolved
Outdated
emma
commented
Remember to handle the possible errors here! Remember to handle the possible errors here!
|
||||
case '1': eof = '\n'; break;
|
||||
case 'n': include_eof = 1; break;
|
||||
case 'o': outputs[0] = stdout; break;
|
||||
case 'e': outputs[1] = stderr; break;
|
||||
case 't':
|
||||
if(isatty(STDIN_FILENO) != 1){
|
||||
fprintf(stderr, "%s: Must be run in a terminal"
|
||||
" (option -t specified)\n", argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
case 'i': allow_nonterminals = 1; break;
|
||||
default: return usage(argv[0]);
|
||||
}
|
||||
|
||||
if(argc > optind)
|
||||
return usage(argv[0]);
|
||||
|
||||
if(!allow_nonterminals && isatty(STDIN_FILENO) != 1){
|
||||
fprintf(stderr, "%s: Must be run in a terminal"
|
||||
" (option -i skips this check)\n", argv[0]);
|
||||
return EX_USAGE;
|
||||
}
|
||||
}
|
||||
|
||||
{ /* install signal handler */
|
||||
@ -116,15 +107,10 @@ int main(int argc, char *argv[]){
|
||||
|
||||
{ /* actual input loop */
|
||||
int c;
|
||||
size_t i;
|
||||
|
||||
do{ if((c = getc(stdin)) != eof || include_eof)
|
||||
for(i = 0; i < (sizeof outputs)/(sizeof *outputs); ++i)
|
||||
if(outputs[i] != NULL && putc(c, outputs[i]) == EOF){
|
||||
perror(argv[0]);
|
||||
outputs[i] = NULL;
|
||||
}
|
||||
}while(c != eof);
|
||||
while((c = getc(stdin)) != EOF)
|
||||
if(putc(c, stdout) == EOF)
|
||||
return ioerr(argv[0]);
|
||||
}
|
||||
|
||||
restore_echo(0);
|
||||
|
Read input from the standard input with terminal echo disabled.
Should this be placed in a RATIONALE section?