📄 pgpuserio.c
字号:
/*
* pgpUserIO.c -- PGP TTY user IO interfaces
*
* Copyright (C) 1995-1997 Pretty Good Privacy, Inc. All rights reserved.
*
* Written by: Derek Atkins <warlord@MIT.EDU> and Colin Plumb
* Modified by Brett A. Thomas <quark@baz.com> to use /dev/random
*
* $Id: pgpUserIO.c,v 1.5.2.10.2.5 1997/07/15 21:26:32 quark Exp $
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#if HAVE_SYS_TIME_H
#include <sys/time.h>
#if !TIME_WITH_SYS_TIME
#include <time.h>
#endif
#endif
#include <sys/types.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#if HAVE_STDARG_H
#include <stdarg.h>
#endif
#if HAVE_SYS_STAT_H
#include <sys/stat.h> /*for stat() to check for /dev/random*/
#endif
#include "pgpDebug.h"
#include "pgpFileRef.h"
#include "pgpAnnotate.h"
#include "pgpESK.h"
#include "pgpFileMod.h"
#include "pgpFixedKey.h"
#include "pgpMem.h"
#include "pgpPassCach.h"
#include "pgpErr.h"
#include "pgpFile.h"
#include "pgpMsg.h"
#include "pgpUI.h"
#include "pgpPipeline.h"
#include "pgpPubKey.h"
#include "pgpRndPool.h"
#include "pgpRngPub.h"
#include "pgpRngRead.h"
#include "pgpSig.h"
#include "pgpTimeDate.h"
#include "pgpUsuals.h"
#include "pgpEnv.h"
#include "pgpKB.h"
#include "pgpUserIO.h"
#include "pgpOutput.h"
#include "pgpRingUI.h"
#include "pgpMoreMod.h"
#include "pgpTypes.h"
/* Filename for dumping data we don't want to keep */
#ifdef MSDOS
#define NULLNAME "NUL"
#else
#define NULLNAME "/dev/null"
#endif
static unsigned
pgpDevRandomAccum(int fd, unsigned count);
static Boolean
CheckDevRandom(char **RandomDevice, PgpTtyUI *ui);
static Boolean
VerifyRandomDevOSVersion(void);
/*
* Get a string from the keyboard into the supplied buffer.
* If echo is true, echos to the InteractionOutput stream.
* Otherwise prints nothing (and beeps go to stderr).
* Returns the number of characters read (always <= len-1).
*/
int
pgpTtyGetString(char *buf, int len, int echo)
{
int c;
int n = 0;
kbCbreak();
#ifdef MACINTOSH
echo = NULL; /* XXX: For now, the Mac version echoes by itself */
#endif
for (;;) {
c = kbGet();
if (c == '\r' || c == '\n') {
c = '\n';
break;
}
else
if (c == '\b' || c == 7 || c == 127) {
if (n > 0) {
n--;
if (echo)
InteractionOutputString(FALSE, "\b \b");
}
else
InteractionOutputString(FALSE, "\a");
}
else
if (c < ' ' || c > 256) {
InteractionOutputString(FALSE, "\a");
}
else
if (n + 1 >= len) {
InteractionOutputString(FALSE, "\a");
kbFlush(0);
}
else {
if (echo)
InteractionOutputString(FALSE, "%c", c);
buf[n++] = (char)c;
}
}
if (echo) {
InteractionOutputString(FALSE, "\n");
}
buf[n] = '\0';
kbNorm();
return n;
}
/*
* Prompts the user for Y or N (case insensitive), and returns
* the appropriate boolean value. If nothing is entered, <def>
* is returned. This assumes that a prompt has already been printed.
*/
int
pgpTtyGetBool(int def, int echo)
{
char buf[2];
for (;;) {
if (pgpTtyGetString(buf, sizeof (buf), echo) == 0)
return def;
switch (buf[0]) {
case 'y':
case 'Y':
return 1;
case 'n':
case 'N':
return 0;
default:
break;
}
InteractionOutput(TRUE,
"ENTER_Y_OR_N",
def ? 'Y' : 'N');
}
}
static void
pgpTtyPutKeyID (byte const *keyid)
{
InformationOutputString(FALSE,
"0x%2.2X%2.2X%2.2X%2.2X\n",
keyid[4],
keyid[5],
keyid[6],
keyid[7]);
}
/*
* Ask the user for a filename.
* string is the default print string that should be used. filename is
* the default that will be used. buffer is of size buflen, and is
* used to store the new filename.
*
* returns the length of the contents of buffer, which must be a real
* filename (trailing white space must be stripped) */
static int
pgpTtyNeedFile (char *buffer, int buflen)
{
char *p;
*buffer = '\0';
pgpTtyGetString (buffer, buflen, 1);
p = buffer + strlen(buffer) - 1;
while (p >= buffer && isspace (*p))
*p-- = '\0';
return (strlen (buffer));
}
static void
pgpTtySigResult (void *arg, union RingObject *key, word32 timestamp,
int status)
{
struct PgpTtyUI *ui = (struct PgpTtyUI *) arg;
struct RingSet const *set = ui->ringset;
char timedate[21];
pgpTimeString (timestamp, timedate);
/* Status tells us whether the sig is good or bad. Must also determine
if the signing key is valid. */
if (status > 0) {
InformationOutput(TRUE, "GOOD_SIGNATURE", timedate);
ringKeyPrint (OUTPUT_INFORMATION, set, key, 1);
ringTtyKeyOKToSign(set, key);
}
else if (status == 0) {
InformationOutput(TRUE, "BAD_SIGNATURE", timedate);
ringKeyPrint (OUTPUT_INFORMATION, set, key, 1);
}
else {
char *ErrorString;
if(PGPErrCodeLoadString(&ErrorString, status) == PGPERR_OK) {
InformationOutput(TRUE,
"ERROR_SIGNATURE",
status,
ErrorString);
FreeString(&ErrorString);
}
PGPErrCodeOutput(TRUE, LEVEL_INFORMATION, status);
}
}
int
pgpTtyGetPass (int showpass, char *buffer, int buflen)
{
int i;
InteractionOutput(TRUE, "ENTER_PASSPHRASE");
i = pgpTtyGetString (buffer, buflen, showpass);
InteractionOutputString(FALSE, "\n");
return i;
}
/*
* Responds to a commit request depending on the TTY UI member "commits"
* If commits >= 0, then it will process that many levels of scopes.
* if commits < 0, it will always recurse.
*/
int
pgpTtyDoCommit (void *arg, int scope)
{
struct PgpTtyUI *ui = (struct PgpTtyUI *)arg;
(void)scope;
if (!ui->commits)
return PGPANN_PARSER_PASSTHROUGH;
ui->commits--;
return PGPANN_PARSER_RECURSE;
}
/*
* Performs an accumulation of random bits. As long as there are
* fewer bits in the buffer than are needed, prompt for more.
* (kbGet is known to call pgpRandPoolKeystroke() which increments
* trueRandBits.)
*/
void
pgpTtyRandAccum (void *arg, unsigned count)
/* Get this many random bits ready */
{
struct PgpTtyUI *ui = (struct PgpTtyUI *)arg;
word32 randbits = pgpRandPoolEntropy ();
Boolean HasDevRandom = FALSE, UseDevRandom = TRUE;
char *RandomDevice = NULL;
int fdDevRandom = -1, fdKB = -1, LargestFD = -1, SelectReturn = 0;
fd_set AllDevicesSet;
if (count > (unsigned)pgpRandPoolSize ())
count = pgpRandPoolSize ();
if (randbits >= count)
return;
/*Open the keyboard so that we can do a select on it:*/
fdKB = kbOpenKbd(O_RDONLY); /*Can't fail*/
FD_SET(fdKB, &AllDevicesSet);
LargestFD = fdKB;
if((HasDevRandom = CheckDevRandom(&RandomDevice, ui))) {
/*
*XXX Need to add support for ForceRandomDevice to pgp.cfg.
*/
if(VerifyRandomDevOSVersion()) {
if((fdDevRandom = open(RandomDevice, O_RDONLY)) >= 0) {
FD_SET(fdDevRandom, &AllDevicesSet);
if(fdDevRandom > LargestFD)
LargestFD = fdDevRandom;
HasDevRandom = TRUE;
}
else
HasDevRandom = FALSE;
}
else {
UseDevRandom = FALSE;
}
}
if(HasDevRandom) {
if(UseDevRandom) {
InteractionOutput(TRUE,
"RANDOM_BITS_FROM_DEVICE",
count - (randbits), RandomDevice);
}
else {
InteractionOutput(TRUE, "RANDOM_BITS_FROM_DEVICE_OLD_KERNEL");
}
}
if(!HasDevRandom || !UseDevRandom) {
InteractionOutput(TRUE,
"RANDOM_BITS_FROM_KEYBOARD",
count - (randbits));
}
kbCbreak ();
do {
/* display counter to show progress */
InteractionOutputString(FALSE,
"\r%4u",
count-(unsigned)(randbits));
FD_ZERO(&AllDevicesSet);
FD_SET(fdKB, &AllDevicesSet);
if(HasDevRandom && UseDevRandom && fdDevRandom >= 0) {
FD_SET(fdDevRandom, &AllDevicesSet);
}
kbFlush (0); /* Typeahead is illegal */
SelectReturn =
select(LargestFD + 1, &AllDevicesSet, NULL, NULL, NULL);
/*If the waiting input is on stdin, or there was some kind of
*error, grab input from the keyboard:
*/
if(SelectReturn < 0 ||
(SelectReturn >= 0 && FD_ISSET(fdKB, &AllDevicesSet))) {
(void)kbGet (); /* Wait for next char */
}
else { /*The waiting data is in /dev/random*/
pgpDevRandomAccum(fdDevRandom, 1);
}
fputc (pgpRandPoolEntropy () == randbits ? '?' : '.', ui->fp);
randbits = pgpRandPoolEntropy ();
} while (randbits < count);
/* Do final display update */
fputs ("\r 0 *", ui->fp);
fputs ("\a -Enough, thank you.\n", ui->fp);
/* Do an extra-thorough flush to absorb extra typing. */
kbFlush (1);
kbNorm ();
if(RandomDevice) {
pgpFree(RandomDevice);
}
if(fdDevRandom >= 0) {
close(fdDevRandom);
}
kbCloseKbd(fdKB); /*Doesn't close if we're on stdin*/
}
int
pgpTtyNeedInput (void *arg, struct PgpPipeline *head)
{
struct PgpTtyUI *ui = (struct PgpTtyUI *) arg;
struct PgpFileRead *fileread;
FILE *file = NULL;
unsigned i, err;
char namebuf[256], suggested_name[256];
if (pgpenvGetInt (ui->env, PGPENV_BATCHMODE, NULL, NULL)) {
if (ui->outname &&
(file = fopen (ui->outname, "rb")) != NULL) {
ui->outname = NULL;
}
else {
ErrorOutput(TRUE, LEVEL_CRITICAL, "NO_INPUT_FILE_IN_BATCHMODE");
return PGPERR_NO_FILE;
}
} else {
if (ui->outname) {
strncpy (suggested_name, ui->outname,
sizeof(suggested_name));
}
else {
suggested_name[0] = '\0';
}
for (;;) {
InteractionOutput(TRUE, "NEED_SIG_FILE", suggested_name);
i = pgpTtyNeedFile (namebuf, sizeof(namebuf));
if (!i) {
if (suggested_name[0])
strcpy (namebuf, suggested_name);
else
return PGPERR_NO_FILE;
}
file = fopen (namebuf, "rb");
if (file)
break;
ErrorOutput(TRUE, LEVEL_CRITICAL, "UNABLE_TO_OPEN_FILE", namebuf);
suggested_name[0] = 0;
}
/* Only use ui->outname once */
if (strcmp (namebuf, ui->outname) == 0)
ui->outname = NULL;
}
fileread = pgpFileReadCreate (file, 1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -