pgpprsasc.c
来自「著名的加密软件的应用于电子邮件中」· C语言 代码 · 共 2,441 行 · 第 1/5 页
C
2,441 行
/*
* pgpPrsAsc.c -- ascii dearmor parser. This is fairly complicated. Read
* the comment a page down for a description of how this system works.
* The nice part is that you can input multipart armor in any order
* and it will work. In fact, you can intersperse armor parts from
* multiple messages and it will work. Moreover, you can input binary
* PGP messages and it will still work! My god, can this man think
* of everything or what?
*
* Copyright (C) 1995-1997 Pretty Good Privacy, Inc. All rights reserved.
*
* Written by: Derek Atkins <warlord@MIT.EDU>
*
* $Id: pgpPrsAsc.c,v 1.13.2.3 1997/06/07 09:50:57 mhw Exp $
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <ctype.h>
#include <stdio.h>
#include <string.h>
/* [Hal] I am working on adding PGP/MIME parsing to this. */
#ifndef MIMEPARSE
#define MIMEPARSE 0
#endif
#include "pgpDebug.h"
#include "pgpCharMap.h" /* for charMapIdentity */
#include "pgpCopyMod.h"
#include "pgpCRC.h"
#include "pgpPrsAsc.h"
#include "pgpRadix64.h"
#include "pgpAnnotate.h"
#if MIMEPARSE
#include "pgpDeMimeMod.h"
#endif
#include "pgpFIFO.h"
#include "pgpFileType.h"
#include "pgpHash.h"
#include "pgpJoin.h"
#include "pgpMem.h"
#include "pgpPrsBin.h"
#include "pgpEnv.h"
#include "pgpErr.h"
#include "pgpPipeline.h"
#include "pgpSplit.h"
#include "pgpTextFilt.h"
#include "pgpUsuals.h"
#include "pgpVerifyRa.h"
/*
* This is a complicated piece of code. Less politely, this code is a
* bitch. It needs to be able to keep track of many different things
* at the same time in order to combine pieces of armor into whole messages.
*
* There are three entities defined here. First is an armor part, which
* is defined as the range from BEGIN PGP MESSAGE to the END. Second is
* a message, which is PGP message made up of one or more parts.
* And third, there are files, which can contain parts.
*
* Parts must be fully contained within a file, however there can be
* multiple parts in a single file. On the other hand, messages can
* cross file boundaries.
*
* This code will output PGP messages in a stream of input "files"
* wherever the first part of the message came in the stream. In a
* multipart message, this will require the least amount of buffering
* since buffering is only required when pieces come out of order.
* This can also handle the degenerate cases of interleaved messages,
* although that requires possibly large amounts of buffering.
* That case happens rarely enough that the resource requirement is
* not onsidered a problem.
*
* A picture best describes this behavior:
*
* W1 W2 X1 X2 X3 Y1 Y2 Y3 Z1 Z2
* | | A2 | | | | B | | A1 | | | | C2 | | C1 | | | |A3| |
* | +--------+ | | +---+ +----+ | | +----+ +----+ | | +--+ |
* +--------------+ +-----------------+ +------------------+ +--------+
* File 1 File 2 File 3 File 4
* ^ ^
* | Everything is buffered |
* +----------------------------+
*
* W,X,Y,Z are file header and footer text which is outside the PGP armor.
* The numbers are used to associate where in the data stream they occur.
*
* A,B,C are 3 PGP messages. The numbers are the actual multipart parts
* which may occur out of order.
*
* If this input stream were given to this armor parser, the following
* output would occur:
*
* | W1 ~ W2 | X1 B X2 A X3 | Y1 ~ Y2 C Y3 | Z1 ~ Z2 |
*
* Where: | == A file separation annotation (or multiple annotations)
* ~ == An armor annotation (an armor part occured here)
* W,X,Y,Z == The exact unencrypted text that arrived in the
* input.
* A,B,C == The full messages, wrapped in "PGP data" annotations,
* which will be sent to a binary parser for decryption.
*
* As you can see, the messages are output in the order B, A, C, each
* message occurring in the place in the unenecrpted text stream where
* part 1 is located. Other parts are replaced with an annotation that just
* notes the deletion.
*
* While decryting message A, all the data between A1 and A3 (including
* annotations) needs to be buffered. In the usual case where the text
* between parts is short (e-mail headers, mostly) and there aren't
* multiple interleaved parts, not very much needs to be buffered.
* Not much can be done to improve the worst case.
*/
#define kPGPPrsAscCmdBufSize 4096
#define DEARMORMAGIC 0xdea4304
/* List of commands */
#define CMD_WRITE 1
#define CMD_ANNOTATE 2
#define LINE_LEN 256 /* 256 bytes is MORE than Ascii armorsize! */
struct Message; /* forward reference */
struct MsgPart {
unsigned num; /* Which part number is this? */
struct PgpPipeline *mod; /* Pointer to the join module for this part */
struct PgpPipeline *text; /* clearsigned: the text goes here */
struct PgpPipeline *sig; /* clearsigned: the sig goes here */
struct Message *msg; /* What message does this belong to? */
struct MsgPart *next; /* Pointer to the next part */
byte first; /* Is this the first section left to do? */
byte done; /* Is this part complete? */
};
struct Message {
char *name; /* The name of this message */
unsigned size; /* How many parts does this have, if known */
struct PgpPipeline **tail; /* Tail pointer for this message */
struct MsgPart *parts; /* The message parts */
struct Message *next; /* The next message */
int msg_number; /* What is this message number */
byte writing; /* Are we writing? */
byte foundpart1; /* Did we find part 1? */
};
struct Context {
struct PgpFifoDesc const *fifod; /* Fifo Descriptor */
struct PgpFifoContext *ann; /* Fifo to hold the saved annotations */
struct PgpFifoContext *data; /* Fifo to hold saved non-PGP data */
struct Message *msgs; /* List of current messages */
struct MsgPart *part; /* The current message part */
struct PgpPipeline *myself; /* Pointer to this pipeline module */
struct PgpPipeline *tail; /* Tail pointer for rest of the pipeline */
struct PgpUICb const *ui;
struct PgpEnv const *env;
void *ui_arg;
unsigned long written; /* Fifo delta from last command */
unsigned long left; /* Bytes left in fifo until next command */
unsigned thispart; /* This part, obtained from the PGP header */
unsigned maxparts; /* Max parts, obtained from the PGP header */
int state; /* FSM state */
int annotation; /* Annotation State */
int depth_at_ann; /* Scope depth when we sent the annotation */
int scope_depth; /* Count of scopes we're inside */
char messageid[LINE_LEN]; /* Message ID, obtained from the PGP header*/
byte databuf[51]; /* Space to hold raw data 48+slush */
byte *dataptr; /* Pointer into the data buffer */
unsigned datalen; /* length of data buffer */
byte armorline[LINE_LEN]; /* line of armor */
byte *armorptr; /* pointer into armorline */
unsigned armorlen; /* length of line */
unsigned long crc; /* CRC Checksum */
byte hashlist[255]; /* List of hashes */
byte hashlen; /* Length of the list of hashes */
byte cmdarg[kPGPPrsAscCmdBufSize]; /* buffered command */
unsigned cmdlen; /* length of buffered command */
int msg_count; /* A count of the number of messages */
byte command; /* the command that is buffered */
byte buffering; /* A flag -- are we buffering everything? */
byte eol; /* Our readLine EOL flag */
byte eob; /* End of Buffer flag (from readLine) */
byte expectcrc; /* Are we expecting the CRC line next? */
byte crlf; /* The type of crlf we have */
byte saved_crlf; /* Save it off... */
byte firstheader; /* True if looking for 1st header line */
byte doublespace; /* True if every other line is blank */
#if MIMEPARSE
byte mime_signed; /* Dealing with a PGP/MIME clearsigned message */
byte no_mime_headers; /* Don't have mime headers, look in body */
byte mime_boundary[256]; /* MIME boundary with "--" */
int mime_bound_len; /* strlen(mime_boundary) */
#endif
};
static char dearmorTable[256];
static int inited = 0;
/* forward references */
static int writeMessage (struct Context *ctx, struct Message *msg);
static int sendAnnotate (struct Context *ctx, struct PgpPipeline *origin,
int type, byte const *string, size_t size);
static int flushMessage (struct Context *ctx, struct Message *msg);
/*
* Close this part. This makes sure that this message is being written
* before it does this. It will call a sizeAdvise (0) on the join
* module and then possibly a teardown, too.
*/
static int
closePart (struct MsgPart *part)
{
int error;
if (! part->msg->writing)
return 0;
if (part->sig) {
error = part->sig->sizeAdvise (part->sig, 0);
if (error)
return error;
}
if (part->mod) {
error = part->mod->sizeAdvise (part->mod, 0);
if (error)
return error;
}
if (part->text) {
error = part->text->sizeAdvise (part->text, 0);
if (error)
return error;
}
part->done = 2;
return 0;
}
/*
* Free a message part; this tears down the join module, which must be
* unhooked from the pipeline first.
*/
static void
freePart (struct MsgPart *part)
{
struct MsgPart **partp = &part->msg->parts;
if (part->mod)
part->mod->teardown (part->mod);
if (part->text)
part->text->teardown (part->text);
if (part->sig)
part->sig->teardown (part->sig);
while (*partp) {
if (*partp == part) {
*partp = part->next;
memset (part, 0, sizeof (*part));
pgpMemFree (part);
return;
}
}
/* no match found */
return;
}
/*
* We are done with the current part, so mark it as done. We should
* also close this part if we are writing this message and either
* there is a next part or this is the last part. Then try to flush
* the message, in case we have anything buffered. Finally, clear the
* part from the context so we know we don't have a current part.
*/
static int
donePart (struct Context *ctx)
{
struct MsgPart *part = ctx->part;
int error;
pgpAssert (part);
part->done = 1;
if (part->msg->writing) {
if (part->next || part->num == part->msg->size) {
error = closePart (part);
if (error)
return error;
}
error = flushMessage (ctx, part->msg);
if (error)
return error;
}
ctx->part = NULL;
return 0;
}
/*
* We're going to start a new part. Therefore we need to pass in the
* message that it is a part of, and then create the new part or
* return the pre-created part. If we ask for a part number in this
* message that is beyond the highest number in the list, then we
* create that many more parts up to the number passed in.
*
* Should we close old parts if we can? I don't know, yet. If we've
* already written out part one and we just got part two, should I close
* part one here? I don't know, yet.
*/
static struct MsgPart *
getPart (struct Context *ctx, struct Message *msg, unsigned num)
{
struct MsgPart *temp = NULL, **part = &msg->parts;
struct PgpPipeline *last = NULL;
unsigned highest = 0;
/* Initialize the CRC counter! */
ctx->crc = CRC_INIT;
while (*part) {
if (num == (*part)->num)
return *part;
highest = (*part)->num;
last = (*part)->mod;
part = &(*part)->next;
}
/* We need to allocate a bunch of parts */
for (highest++; highest <= num; highest++) {
temp = (struct MsgPart *)pgpMemAlloc (sizeof (*temp));
if (!temp)
return NULL;
memset (temp, 0, sizeof (*temp));
temp->num = highest;
temp->msg = msg;
if (last) {
temp->mod = pgpJoinAppend (last);
} else {
msg->tail = pgpJoinCreate (&(temp->mod), ctx->fifod);
temp->first = 1;
msg->tail = pgpParseBinCreate (msg->tail, ctx->env);
/*
* Binary parser may add modules after itself, and in some
* error recovery situations they are still there when we try
* to close things. Create a copy module to act as a stable
* predecessor to context->tail.
*/
msg->tail = pgpCopyModCreate (msg->tail);
/* connect the join module to the rest of the pipe */
if (msg->tail)
*(msg->tail) = ctx->tail;
}
if (!temp->mod) {
pgpMemFree (temp);
return NULL;
}
*part = temp;
last = temp->mod;
part = &temp->next;
}
return temp;
}
/*
* We have a part of a message "name" of size "size" -- lets try to
* find the message that this is a part of, and either return that
* message pointer or create a new one and return that. If name is
* non-NULL, then it will compare the name passed in with older names
* (i.e., a messageID). If name is NULL, then it will compare sizes
* for old-style multipart armor. Do not return messages that have
* only one part.
*
* This means that you can only have one old-style multipart message
* going of any particular size (number of parts). I don't consider
* this a big problem, since interleaved messages are rarely a
* problem. Out-of-order messages, on the other hand.....
*/
static struct Message *
getMessage (struct Context *ctx, char const *name, unsigned size)
{
struct Message **msg = &ctx->msgs;
char *newname;
/* Just remove name if it is a NULL string */
if (name && !*name)
name = NULL;
if (!name && !size)
return NULL;
while (*msg) {
if (name && (*msg)->name && !memcmp (name, (*msg)->name,
strlen (name))) {
/* Update the message size if we know it */
if (!(*msg)->size && size)
(*msg)->size = size;
return *msg;
}
if (!name && !(*msg)->name && size == (*msg)->size &&
size != 1)
return *msg;
msg = &((*msg)->next);
}
*msg = (struct Message *)pgpMemAlloc (sizeof (**msg));
if (! *msg)
return NULL;
memset (*msg, 0, sizeof (**msg));
if (name) {
newname = (char *)pgpMemAlloc (strlen (name) + 1);
if (!newname) {
pgpMemFree (*msg);
*msg = NULL;
return NULL;
}
memcpy (newname, name, strlen (name) + 1);
} else
newname = NULL;
(*msg)->name = newname;
(*msg)->size = size;
(*msg)->msg_number = ++(ctx->msg_count);
return *msg;
}
/*
* We're done with this message so we can remove it from our queue
* of pending messages. Free any remaining message parts in the process.
* We can also turn buffering off now.
*/
static void
freeMessage (struct Context *ctx, struct Message *msg)
{
struct Message **msgp = &ctx->msgs;
struct MsgPart *part, *temp;
while (*msgp) {
if (*msgp == msg) {
*msgp = msg->next;
part = msg->parts;
if (msg->tail)
/* Disconnect join module */
*(msg->tail) = NULL;
while (part) {
temp = part;
part = part->next;
freePart (temp);
}
if (msg->name)
pgpMemFree (msg->name);
ctx->buffering = 0;
memset (msg, 0, sizeof (*msg));
pgpMemFree (msg);
return;
}
}
/* no match found */
return;
}
/*
* Buffer a command at the current spot in the data stream. This is only
* called when ctx->buffering is true. What it does is find the delta
* of this command in the data fifo from the last command in order to replay
* the commands at the proper place. It saves off context->written as
* the offset and then resets the value to 0.
*
* The return value is 0 on success or an error code.
*
* Command Byte Syntax: <offset><cmd><arglen><arg>
*/
static int
bufferCommand (struct Context *ctx, byte cmd, const byte *arg, unsigned arglen)
{
/* Make sure the command argument fits the buffer size */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?