📄 pgp.c
字号:
/*
* pgp.c - PGP Main routine
*
* Copyright (C) 1995-1997 Pretty Good Privacy, Inc. All rights reserved.
*
* Written by: Derek Atkins <warlord@MIT.EDU>
*
* Extensively rewritten by: Brett A. Thomas (bat@pgp.com, quark@baz.com)
*
* Others involved include Hal Finney, Colin Plumb and Mark Weaver.
*
* $Id: pgp.c,v 1.1.2.4.2.9 1997/07/15 21:26:19 quark Exp $
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
#include <string.h>
#include <time.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h> /* for sbrk() */
#endif
#ifdef UNIX
#include <sys/stat.h> /* for umask() */
#endif
#if _MSC_VER>=1000
#include <limits.h> /* for PATH_MAX */
#endif
#include <ctype.h>
#include "pgpDebug.h"
#include "pgpArmrFil.h"
#include "pgpBufMod.h"
#include "pgpCipher.h"
#include "pgpConvKey.h"
#include "pgpDecPipe.h"
#include "pgpEncPipe.h"
#include "pgpFIFO.h"
#include "pgpFileMod.h"
#include "pgpFileNames.h"
#include "pgpFileType.h"
#include "pgpHash.h"
#include "pgpMem.h"
#include "pgpPassCach.h"
#include "pgpPipeline.h"
#include "pgpConf.h"
#include "pgpEnv.h"
#include "pgpErr.h"
#include "pgpFile.h"
#include "pgpUI.h"
#include "pgpPubKey.h"
#include "pgpRndom.h"
#include "pgpRngPub.h"
#include "pgpRngRead.h"
#include "pgpSigSpec.h"
#include "pgpOutput.h"
#include "pgpUserIO.h"
#include "pgpRingUI.h"
#include "pgpUsuals.h"
#include "pgpTrstPkt.h"
#include "pgpTrust.h"
#include "pgpExit.h"
#include "pgpAppFile.h"
#include "pgpKeyRings.h"
#include "pgpInitApp.h"
#include "pgpOpt.h"
#include "pgpRndPool.h"
#define PGP_MAIN /*So globals get assigned*/
#include "parsearg.h"
#include "pass.h"
typedef enum _PGPInvokedType {
Unknown = 0,
PGPencrypt,
PGPsign,
PGPverify,
PGPold,
PGPraw,
PGPkeys
}PGPInvokedType;
static PGPInvokedType getInvokedType(char *Argv0);
static void exitInvokedRaw(void);
static struct PgpPipeline *head = NULL;
static struct PgpRandomContext *rng = NULL;
static struct PgpSecKey *secretKey = NULL;
static struct PgpPubKey *publicKey = NULL;
static struct RingPool *ringpool = NULL;
static struct PgpFile *
openFile (void *arg, void const *base, unsigned num)
{
FILE *fp;
char filename[PATH_MAX+1];
char n[20];
memcpy (filename, base, strlen ((char *)base)+1);
#ifdef MSDOS
if (num > 1) {
char *name = fileNameContract(filename);
strcpy (filename, name);
pgpMemFree(name);
if (num < 10)
sprintf (n, ".as%d", num);
else if (num < 100)
sprintf (n, ".a%d", num);
else {
pgpAssert (num < 1000);
sprintf (n, ".%d", num);
}
strcat (filename, n);
}
#else
if (num) {
sprintf (n, ".%d", num);
strcat (filename, n);
}
#endif
/* Do check for overwrite on single part file, or first part of
multi-part file only. */
if (num <= 1 && pgpTtyCheckOverwrite (arg, filename))
return NULL;
StatusOutput(TRUE, "CREATING_OUTPUT_FILE", filename);
fp = fopen (filename, "wb");
if (!fp)
return NULL;
return pgpFileWriteOpen (fp, NULL);
}
typedef struct AlgVotes
{
int numAlgs;
int numVoters;
/* Arrays indexed by algorithm number */
int * algOkay; /* Number of voters who can accept indexed alg */
int * algVotes; /* Preference voting, lower is better */
} AlgVotes;
static PGPError
InitAlgVotes(AlgVotes *algVotes)
{
size_t arraySize;
algVotes->numAlgs = 1;
algVotes->numVoters = 0;
while (pgpCipherByNumber(algVotes->numAlgs))
algVotes->numAlgs++;
arraySize = sizeof(int) * algVotes->numAlgs;
algVotes->algOkay = (int *)pgpAlloc(arraySize);
if (algVotes->algOkay == NULL)
return PGPERR_NOMEM;
algVotes->algVotes = (int *)pgpAlloc(arraySize);
if (algVotes->algVotes == NULL)
{
pgpFree(algVotes->algOkay);
return PGPERR_NOMEM;
}
pgpClearMemory(algVotes->algOkay, arraySize);
pgpClearMemory(algVotes->algVotes, arraySize);
return PGPERR_OK;
}
static void
CleanupAlgVotes(AlgVotes *algVotes)
{
pgpFree(algVotes->algOkay);
pgpFree(algVotes->algVotes);
}
static void
AccumAlgVotes(AlgVotes *algVotes, byte const *prefAlgs, size_t numPrefAlgs)
{
size_t i;
algVotes->numVoters++;
if (prefAlgs && numPrefAlgs > 0)
{
for (i = 0; i < numPrefAlgs; i++)
if (prefAlgs[i] < algVotes->numAlgs)
{
algVotes->algVotes[prefAlgs[i]] += i;
algVotes->algOkay[prefAlgs[i]]++;
}
}
else
{
algVotes->algOkay[PGP_CIPHER_IDEA]++;
}
}
/* Returns voted algorithm, or 0 if none is acceptable */
static byte
ChooseAlgFromVotes(AlgVotes const *algVotes)
{
int i;
int bestAlg = 0;
int bestVote = INT_MAX;
for (i = 1; i < algVotes->numAlgs; i++)
{
if (algVotes->algOkay[i] == algVotes->numVoters &&
algVotes->algVotes[i] < bestVote)
{
bestVote = algVotes->algVotes[i];
bestAlg = i;
}
}
return bestAlg;
}
/* Call pgpk to add keys */
static int
mainAddKeys (void *arg, char const *filename)
{
static char const *prefix = "pgpk -a ";
struct PgpTtyUI *ui_arg = (struct PgpTtyUI *)arg;
struct RingSet *tset;
char *buf;
int err;
(void)arg;
buf = pgpMemAlloc(strlen(prefix) + strlen(filename) + 1);
if (!buf)
return PGPERR_NOMEM;
/*
* Run pgpk with keyrings closed. This is for two reasons.
* First, some OS's won't allow renaming and such of open files
* (e.g. MSDOS) and so we must close ours so pgpk can add keys.
* Second, this way when we re-open our keyrings we will get
* the new ones with the keys added, which may help in further
* processing the input.
*/
mainCloseKeyrings (1, 0);
StatusOutput(TRUE, "COPYING_KEYFILE_AND_RUNNING_PGPK", filename);
strcpy(buf, prefix);
strcat(buf, filename);
fprintf(stderr, "%s\n", buf);
err = system(buf);
pgpMemFree(buf);
/* Re-open keyrings */
(void)mainOpenKeyrings (ui_arg->env, ringpool, 0, &tset);
ui_arg->ringset = tset;
if (err)
return PGPERR_GENERIC;
return 0;
}
static size_t
mainGetPass (char *passp, struct UIArg *ui_arg)
{
char pass[PASSLEN];
char pass2[PASSLEN];
int showpass = 0;
showpass = pgpenvGetInt(ui_arg->arg.env, PGPENV_SHOWPASS, NULL, NULL);
InteractionOutput(TRUE, "NEED_PASSPHRASE");
if (pgpenvGetInt (ui_arg->arg.env, PGPENV_BATCHMODE, NULL, NULL))
return 0;
do {
memset (pass, 0, sizeof (pass));
memset (pass2, 0, sizeof (pass2));
pgpTtyGetPass (showpass, pass, sizeof(pass));
InteractionOutput(TRUE, "NEED_PASSPHRASE_AGAIN");
pgpTtyGetPass (showpass, pass2, sizeof(pass2));
if (!strcmp (pass, pass2))
break;
InteractionOutput(TRUE, "PASSPHRASES_DIFFERENT");
} while (1);
if (!strlen (pass)) {
ErrorOutput(TRUE, LEVEL_CRITICAL, "ZERO_LEN_PASSPHRASE");
return 0;
}
memcpy (passp, pass, sizeof (pass));
memset (pass2, 0, sizeof (pass2));
memset (pass, 0, sizeof(pass));
return strlen ((char *)passp);
}
/*
* Setup a pipeline based upon the arguments in the flags
*/
static struct PgpPipeline **
mainSetupPipeline (struct PgpPipeline **pipehead,
struct PgpEnv *env, struct Flags *flags,
struct PgpRandomContext *rng_p, char const *in_name,
struct PgpFileRead *readhead, struct UIArg *ui_arg)
{
struct PgpPipeline *mod = NULL, **tail = &mod;
struct PgpConvKey convkey, *convkeys = NULL;
struct PgpSigSpec *sigspec = NULL;
struct PgpLiteralParams lP, *literalParams = &lP;
byte const *ptr;
size_t len;
extern Boolean RngSeeded;
if (flags->moreflag)
literalParams->filename = "_CONSOLE";
else
literalParams->filename = in_name;
if (flags->conventional) {
size_t passlen;
/* Use passphrase given with -z, else ask for one */
if (phrase[0])
passlen = strlen(phrase);
else
passlen = mainGetPass (phrase, ui_arg);
if (!passlen)
exitCleanup (-1);
convkey.next = NULL;
convkey.stringToKey = 0;
convkey.pass = phrase;
convkey.passlen = passlen;
convkeys = &convkey;
}
/*
* Check the file to see if this is text or binary file. If
* it binary, then force binary mode and check for
* compressibility. If it is not compressible, turn off
* compression, too.
*
* If it is a PGP file, ask the user if it should be treated
* as a PGP file. If it should, then ignore the literal packet
* type.
*/
ptr = pgpFileReadPeek (readhead, &len);
pgpRandomAddBytes (rng_p, ptr, len);
if (pgpFileTypeBinary (pgpenvGetString (env, PGPENV_LANGUAGE,
NULL, NULL),
ptr, len)) {
struct PgpFileType const *ft;
pgpenvSetInt (env, PGPENV_TEXTMODE, 0, PGPENV_PRI_FORCE);
ft = pgpFileType (ptr, len);
if (ft && !ft->compressible)
pgpenvSetInt (env, PGPENV_COMPRESS, 0,
PGPENV_PRI_FORCE);
if (pgpFileTypePGP (ptr, len)) {
if (!pgpenvGetInt (ui_arg->arg.env, PGPENV_BATCHMODE,
NULL, NULL)) {
InteractionOutput(TRUE, "TREAT_AS_PGP");
if (pgpTtyGetBool (0, 1))
/* No literal if this is a PGP msg */
literalParams = NULL;
}
}
}
if (flags->sign) {
union RingObject *ringobj;
char const *myname = pgpenvGetString (env, PGPENV_MYNAME,
NULL, NULL);
int err, arg;
if (!secretKey) {
ringobj = ringLatestSecret (ui_arg->arg.ringset,
myname, time(NULL),
PGP_PKUSE_SIGN);
if (!ringobj) {
ErrorOutput(TRUE,
LEVEL_CRITICAL,
"PRIVATE_KEY_MISSING",
myname);
exitCleanup (-1);
}
secretKey = ringSecSecKey (ui_arg->arg.ringset,
ringobj, PGP_PKUSE_SIGN);
ringObjectRelease (ringobj);
if (!secretKey) {
ErrorOutput(TRUE,
LEVEL_CRITICAL,
"CANNOT_CONVERT_TO_PRIVATE_KEY");
exitCleanup (-1);
}
if (!secretKey->sign) {
ErrorOutput(TRUE, LEVEL_CRITICAL, "PRIVATE_KEY_CANNOT_SIGN");
exitCleanup (-1);
}
err = pgpTtyUnlockSeckey (&ui_arg->arg, ringobj,
secretKey,
"PRIVATE_KEY_NEEDED_FOR_SIGNATURE");
if (err) {
ErrorOutput(TRUE,
LEVEL_CRITICAL,
"CANNOT_UNLOCK_PRIVATE_KEY");
exitCleanup (err);
}
}
arg = pgpenvGetInt (env, PGPENV_TEXTMODE, NULL, NULL);
arg = arg ? PGP_SIGTYPE_TEXT : PGP_SIGTYPE_BINARY;
sigspec = pgpSigSpecCreate (env, secretKey, arg);
if(!RngSeeded && !pgpRandPoolEntropy()) {
pgpTtyRandAccum (ui_arg, 196);
RngSeeded = TRUE;
}
}
/* This will find *ALL* keys that match *ALL* recipients */
if (flags->numrecips) {
struct RingPool * pool = ringSetPool (ui_arg->arg.ringset);
int i = flags->numrecips;
int e;
struct RingSet * ringset;
struct RingIterator * ringiter;
union RingObject * ringobj;
struct PgpPubKey * temp;
AlgVotes algVotes;
byte const * prefAlgs;
size_t numPrefAlgs;
byte bestAlg;
if (!pool) {
ErrorOutput(TRUE,
LEVEL_CRITICAL,
"NO_KEYRINGS");
exitUsage (-1);
}
if (! publicKey) {
char const *companyKey;
ringset = ringSetCreate (pool);
if (!ringset)
exitUsage (ringPoolError (pool)->error);
while (i--) {
e = ringSetFilterSpec (ui_arg->arg.ringset,
ringset,
flags->recips[i],
PGP_PKUSE_ENCRYPT);
if (e==0) {
ErrorOutput(TRUE,
LEVEL_CRITICAL,
"NO_ENCRYPTION_KEYS_FOUND_FOR",
flags->recips[i]);
exitCleanup(-1);
}
if (e <= 0)
exitUsage(e);
if(e > 1)
{
WarningOutput(TRUE,
LEVEL_INFORMATION,
"MULTIPLE_RECIPIENTS_MATCHED",
e,
flags->recips[i]);
}
}
if (pgpenvGetInt (env, PGPENV_ENCRYPTTOSELF,
NULL, NULL)) {
char const *myname =
pgpenvGetString (env, PGPENV_MYNAME,
NULL, NULL);
/* Add myself to the list of recipients */
ringobj = ringLatestSecret
(ui_arg->arg.ringset, myname,
time(NULL), PGP_PKUSE_ENCRYPT);
if (!ringobj) {
ErrorOutput(TRUE,
LEVEL_CRITICAL,
"CANNOT_FIND_KEY",
myname);
exitCleanup (-1);
}
if (ringSetAddObject (ringset, ringobj)) {
ErrorOutput(TRUE,
LEVEL_CRITICAL,
"CANNOT_ADD_MY_KEY");
exitCleanup (-1);
}
}
/*
* This is our version of "Commercial Key Escrow".
*
* A company can set the CompanyKey in the site-wide
* configuration file and it will be added to the
* recipient list for all encrypted messages. Then
* again, the user can override the setting in their
* own pgp.cfg or on the command line.
*/
companyKey = pgpenvGetString (env, PGPENV_COMPANYKEY,
NULL, NULL);
if (companyKey && *companyKey) {
/* Add the company key to the recipient list */
e = ringSetFilterSpec (ui_arg->arg.ringset,
ringset,
companyKey,
PGP_PKUSE_ENCRYPT);
if (e <= 0)
exitUsage(e);
}
ringSetFreeze (ringset);
ringiter = ringIterCreate (ringset);
if (!ringiter)
exitUsage (ringPoolError (pool)->error);
e = InitAlgVotes(&algVotes);
if (e)
exitUsage(e);
while ((e = ringIterNextObject (ringiter, 1)) > 0) {
ringobj = ringIterCurrentObject(ringiter, 1);
if (ringObjectType (ringobj) != RINGTYPE_KEY) {
#if 0
fprintf (stderr,
"Got a ring object of type %d\n",
ringObjectType (ringobj));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -