peek(1): strip down to bare essentials, rewrite man page to match

This commit is contained in:
dtb 2024-04-18 10:03:02 -06:00
parent 46b607384a
commit c7739612a7
Signed by: trinity
GPG Key ID: 31FF85CCB6DC7641
2 changed files with 57 additions and 97 deletions

View File

@ -12,52 +12,17 @@ peek \(en read from standard input, furtively
.SH SYNOPSIS .SH SYNOPSIS
peek peek
.RB ( -1enot ) .RB ( -i )
.RB ( -p
.RB [ program
.RB [ arguments... ]])
.SH DESCRIPTION .SH DESCRIPTION
Peek reads input from standard input with terminal echo disabled, which may be Peek reads input from standard input with terminal echo disabled.
useful to prevent secrets being spied upon by adversaries watching a user's
screen.
.SH OPTIONS .SH OPTIONS
.B -1 .B -i
.RS .RS
Limits input to a single line (stopping when the newline character is read). Allows input to come from sources other than terminals (i.e. a pipe).
.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.
.RE .RE
.SH DIAGNOSTICS .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 prints an error message and continues. If peek is interrupted, it exits
unsuccessfully, without an error message. 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 .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
secrets or mask the sound of typing. Audio or video recordings of typing can be
used to determine what was input without needing to see the characters appear
on the screen.
If used in a safety-critical application it must be ensured that the Accepting secrets in shell scripts is probably not adviseable.
environment with which peek is used is not compromised.
On systems that support it, the ioctl(2) command TIOCSTI can be used to insert 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 characters into the standard input going to peek. This doesn't allow snooping
but can be used for general mischief. but can be used for general mischief.
Peek will happily run without outputs, slurping input and doing nothing with
it.
.SH EXAMPLES .SH EXAMPLES
This is a command line for POSIX sh(1), using POSIX env(1) to discard the This is an sh(1p) command line that hashes a given password. It uses head(1p)
environment except for PATH, and htpassword(1) from Apache's utilities to hash to only accept one line of input, xargs(1p) and printf(1p) to strip the
the input with the bcrypt algorithm, printing the resulting hash: 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 .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 .RE
This is a POSIX sh(1) command line that lets a user blindly write into a text This is an sh(1p) command line that lets a user blindly write into a text file,
file, only able to see written lines. Some writers have the habit of only able to see written lines. Some writers have the habit of prematurely
prematurely revising their work and use tools like this to prevent it: 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
writing.txt.
.RS .RS
.R $ echo Input ^D to quit. && peek -eot >writing.txt .R $ echo Input ^D to quit. && peek | mm -eo - >writing.txt
.RE .RE
.SH AUTHOR .SH AUTHOR
@ -113,4 +87,4 @@ Copyright (c) 2023-2024 DTB. License AGPLv3+: GNU AGPL version 3 or later
.SH SEE ALSO .SH SEE ALSO
env(1), ioctl(2), ioctl_tty(2), read(1), sh(1) ioctl(2), ioctl_tty(2), read(1), sh(1)

View File

@ -17,75 +17,66 @@
*/ */
#include <signal.h> /* sigaction(2), signal(2), struct sigaction, SIGINT */ #include <signal.h> /* sigaction(2), signal(2), struct sigaction, SIGINT */
#include <stdio.h> /* fclose(3), fdopen(3), fprintf(3), getc(3), perror(3), #include <stdio.h> /* fprintf(3), fgetc(3), perror(3), fputc(3), stderr, stdin,
* putc(3), stderr, stdin, stdout, EOF, NULL */ * stdout, EOF, NULL */
#include <stdlib.h> /* exit(3), size_t, EXIT_FAILURE */ #include <stdlib.h> /* exit(3), EXIT_FAILURE */
#include <string.h> /* strerror(3) */
#if !defined EX_OK || !defined EX_OSERR || !defined EX_USAGE #if !defined EX_OK || !defined EX_OSERR || !defined EX_USAGE
# include <sysexits.h> # include <sysexits.h>
#endif #endif
#include <termios.h> /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */ #include <termios.h> /* tcgetattr(3), tcsetattr(3), struct termios, ECHO */
#include <unistd.h> /* getopt(3), isatty(3), STDIN_FILENO */ #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 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 /* 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 * continue to not display typed text. If sig isn't zero, this will terminate
* the program. */ * the program. */
void restore_echo(int sig){ static void restore_echo(int sig){
static struct termios t; static struct termios t;
tcgetattr(STDIN_FILENO, &t); tcgetattr(STDIN_FILENO, &t);
t.c_lflag |= ECHO; t.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &t); 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) if(sig != 0)
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
else
return; return;
} }
int main(int argc, char *argv[]){ static int oserr(char *s){ perror(s); return EX_OSERR; }
int eof; static int ioerr(char *s){ perror(s); restore_echo(0); return EX_IOERR; }
char include_eof; static int usage(char *s){
FILE *outputs[] = { fprintf(stderr, "Usage: %s (-1enot) (-p [program [arguments...]])\n", s);
NULL /* stdout */,
NULL /* stderr */
};
if(argc < 1)
return usage(argv[0]);
eof = EOF;
include_eof = 0;
{ /* options parsing */
int c;
while((c = getopt(argc, argv, "1enot")) != -1)
switch(c){
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; return EX_USAGE;
} }
int main(int argc, char *argv[]){
if(argc < 1)
return usage(program_name);
{ /* options parsing */
char allow_nonterminals;
int c;
allow_nonterminals = 0;
while((c = getopt(argc, argv, "i")) != -1)
switch(c){
case 'i': allow_nonterminals = 1; break;
default: return usage(argv[0]); default: return usage(argv[0]);
} }
if(argc > optind) if(argc > optind)
return usage(argv[0]); 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 */ { /* install signal handler */
@ -116,15 +107,10 @@ int main(int argc, char *argv[]){
{ /* actual input loop */ { /* actual input loop */
int c; int c;
size_t i;
do{ if((c = getc(stdin)) != eof || include_eof) while((c = getc(stdin)) != EOF)
for(i = 0; i < (sizeof outputs)/(sizeof *outputs); ++i) if(putc(c, stdout) == EOF)
if(outputs[i] != NULL && putc(c, outputs[i]) == EOF){ return ioerr(argv[0]);
perror(argv[0]);
outputs[i] = NULL;
}
}while(c != eof);
} }
restore_echo(0); restore_echo(0);