pgpprsasc.c

来自「著名的加密软件的应用于电子邮件中」· C语言 代码 · 共 2,441 行 · 第 1/5 页

C
2,441
字号
		text->teardown (text);
		sighead->teardown (sighead);
		return PGPERR_NOMEM;
	}

	/* Now put it all together in this message */
	part->text = text;
	part->sig = sighead;

	return 0;
}

/*
 * Build up the Ascii Parser Table in order to dearmor data.  For all
 * values, set it to -1 if it is not a valid armor character and then
 * set the valid characters out of the armorTable.  Inited == 0 implies
 * we need to init; Inited == 1 implies we are processing or done.
 */
static void
parseAscInit (void)
{
	int i;

	if (inited++)
		return;

	for (i = 0; i < 256; i++)
		dearmorTable[i] = -1;

	for (i = 0; i < (int)(sizeof(armorTable) - 1); i++)
		dearmorTable[armorTable[i] & 0xff] = (char)i;

	inited = 1;
	return;
}

/*
 * Convert input bytes to output bytes.  Returns the number of
 * input bytes successfully converted.  The number of output
 * bytes available is one less than this number.
 */
static int
dearmorMorsel (byte const in[4], byte out[3])
{
	signed char c0, c1;

	c0 = dearmorTable[in[0] & 255];
	if (c0 < 0)
		return 0;
	c1 = dearmorTable[in[1] & 255];
	if (c1 < 0)
		return 1;

	out[0] = (c0 & 63u) << 2 | (c1 & 63u) >> 4;

	c0 = dearmorTable[in[2] & 255];
	if (c0 < 0)
		return 2;

	out[1] = (c1 & 63u) << 4 | (c0 & 63u) >> 2;

	c1 = dearmorTable[in[3] & 255];
	if (c1 < 0)
		return 3;

	out[2] = (c0 & 63u) << 6 | (c1 & 63u);

	return 4;
}

/*
 * Given a line of a certain length, convert to binary and
 * return the number of binary bytes that result, or -1 on error.
 *
 * This is very fussy about trailing junk and whatnot.
 * There is some complexity due to accepting "=3D" in place
 * of a normal "=".  This is to allow MIME-encapsulated messages
 * to be input directly, without having MIME unencapsulate them
 * first.
 *
 * This will ignore trailing white space and give an error if there
 * is too much data on the line.
 */
static int
dearmorLine (struct Context *ctx, unsigned inlen)
{
	byte const *in = ctx->armorline;
	byte *out = ctx->databuf;
	int outlen = 0;
	int t;

	while ((t = dearmorMorsel (in, out)) == 4) {
		in += 4;
		out += 3;
		inlen -= 4;
		outlen += 3;
		if (inlen < 4)
			return inlen ? -1 : outlen;
	}
	switch (t) {
	  case 2:
		if (inlen == 4) {
			if (in[2] == '=' && in[3] == '=')
				return outlen + 1;
		} else if (inlen == 8) {
			if (in[2] == '=' && in[3] == '3' && in[4] == 'D' &&
			    in[5] == '=' && in[6] == '3' && in[7] == 'D')
				return outlen + 1;
		}
		break;
	  case 3:
		if (inlen == 4) {
			if (in[3] == '=')
				return outlen + 2;
		} else if (inlen == 6) {
			if (in[3] == '=' && in[3] == '3' && in[4] == 'D')
				return outlen + 2;
		}
		break;
	}
	/* None of the above - we have an error */
	return -1;

}

/*
 * Given a line thought to contain a CRC, this returns the 24-bit
 * CRC, or -1 on error.  Handles possible MIME expansion
 * of "=" to "=3D".
 */
static long
dearmorCrc (struct Context *ctx)
{
	byte const *in = ctx->armorline;
	unsigned inlen = ctx->armorlen;
	byte buf[3];

	/* skip trailing white space */
	while (inlen && isspace (in[inlen - 1]))
		inlen--;

	if (*in != '=')
		return -1;
	if (inlen == 5)
		if (dearmorMorsel (in + 1, buf) != 4)
			return -1;
		else if (inlen == 7)
			if (in[1] != '3' || in[2] != 'D' ||
			    dearmorMorsel (in + 3, buf) != 4)
				return -1;
			else
				return -1;
	/*
	 * Welcome to the famous ANSI C glitch.  ANSI C
	 * promotes to signed values where possible when preserving
	 * the value.  Thus, buf[1]<<8 is promoted to signed, then
	 * shifted, then promoted to long (on a 16-bit int machine,
	 * this causes sign-extension!), and merged with the other
	 * values.  Not Good.
	 * I'm beginning to see why people preferred the K&R unsigned-
	 * preserving rules.  Sigh.
	 */
	return (long)buf[0] << 16 | (unsigned)(buf[1] << 8 | buf[2]);
}

/*
 * try to fill up ctx->armorline up to LINE_LEN-1 bytes or a newline,
 * whichever comes first.
 *
 * This will set ctx->eol when an EOL condition occurs.  eol == 1
 * means armorline is ready for processing.  eol == 2 means that
 * readLine needs to be called with more data (looking for \n).
 * In doublespace mode, we use two more values.  eol==3 means that
 * we saw one line ending (LF or CRLF) and now need more data to see
 * another.  eol==4 means that we saw CR-LF-CR and are now looking for
 * the LF.  (doublespace means we skip a blank line after every non-blank
 * line.  A third party application accidentally munges the files to that
 * form.)
 *
 * The return value is the number of bytes used.  If all the bytes are
 * used and EOL is not set, then more data is required and readLine
 * should be called with more data.
 */
static size_t
readLine (struct Context *ctx, byte const *buf, size_t size)
{
	size_t size0 = size;
	unsigned t, retlen;
	byte eol = ctx->eol;
	byte *ptr = ctx->armorline + ctx->armorlen;

	if (!size)
		return 0;

	/*
	 * This test is needed in case the '\r' and '\n' come in
	 * different write calls.
	 */
	if (eol >= 2)
		goto lineend;

	/* try to fill the input buffer with a line */
	t = (unsigned)min (LINE_LEN-1 - ctx->armorlen, size);
	for (eol = 0, retlen = 0; retlen < t && !eol; retlen++) {
		*ptr++ = *buf;
		if (*buf == '\r' || *buf == '\n')
			eol = (*buf == '\r' ? 2 : 1);
		buf++;
	}
	size -= retlen;
	ctx->armorlen += retlen;

	/* Set EOB if we hit the end of the armorline buffer */
	if (ctx->armorlen >= LINE_LEN-1)
		ctx->eob = 1;

	/*
	 * At this point we are either at EOL or not.  If not, then we
	 * either ran out of input data or ran out of buffer space.  If
	 * we are at EOL and eol==2 and we have more data, check if its a
	 * \n or something else. If so, copy that in and set eol=1.
	 * Then do the doublespace logic.  If we get here with eol > 2 that
	 * can only happen from entry, and we use kludgy goto's to fall into
	 * the right place in the eol logic below.
	 */
lineend:
	if (eol) {
		if (eol == 3)
			goto eol3;
		if (eol == 4)
			goto eol4;
		ctx->crlf = (eol == 2 ? PGP_TEXTFILT_CR : PGP_TEXTFILT_LF);

		if (eol == 2 && size) {
			if (*buf == '\n') {
				*ptr++ = *buf++;
				ctx->armorlen++;
				ctx->crlf = PGP_TEXTFILT_CRLF;
				size--;
			}
			eol = 1;
		}
		/*
		 * Don't do the doublespace transformation to blank lines!
		 * This is ad hoc but that is how the failing case presents
		 * itself.  Unfortunately it is hard to know here that we are
		 * looking at a blank line.  The calling code parses lines as soon
		 * as we return with eol > 0.  Then later it calls us to "clean up"
		 * (get to eol==1),
		 * but it has reset the armorlen index to 0.  It becomes hard to
		 * distinguish between a CR-LF at the end of the line, where we got
		 * split between them, and a CR-LF at the beginning.  In each case
		 * our eol > 0 call looks much the same.
		 * To fix this, we have fixed the caller in the relevant portions
		 * (parsing of ascii armored messages/keys) to call us with an
		 * armorlen index of 1 while cleaning up.  This lets us peek at
		 * the original first char of the line and determine whether we
		 * are looking at a blank line.
		 */
		if (ctx->doublespace && eol==1 && ctx->armorline[0]!='\n' &&
			ctx->armorline[0]!='\r') {
			/* Ignore any following blank line */
eol3:
			if (!size) {
				/* LF or CR-LF and no more data.  Remember state 3 */
				eol = 3;
			} else if (*buf == '\n') {
				/* Got a blank line, just ignore it */
				size--;
				eol = 1;
			} else if (*buf == '\r') {
				size--;
				buf++;
				if (ctx->crlf == PGP_TEXTFILT_CR) {
					/* Got a blank line, just ignore it */
					eol = 1;
				} else {
					/* Want to see CR-LF-CR-LF, got the first three... */
eol4:
					if (!size) {
						/* CR-LF-CR and no more data. Remember state 4 */
						eol = 4;
					} else if (*buf == '\n') {
						/* OK, got all four, just ignore the line */
						eol = 1;
						size--;
					} else {
						/* CR-LF-CR-XX, turn off doublespace */
						ctx->doublespace = FALSE;
						size++;	/* Give back CR we took */
						eol = 1;
					}
				}
			} else {
				/* LF or CR-LF then XX */
				ctx->doublespace = FALSE;
				eol = 1;
			}
		}
	}

	/* Save off the EOL and return the number of bytes used */
	ctx->eol = eol;
	return size0-size;
}

/* Work like memcmp but ignore material in angle brackets for 1st arg */
/* On a match (return of 0) put number of removed chars in *removelen */
static int
bracketcmp(const char *orig, size_t origlen, const char *test, size_t testlen,
	size_t *removelen)
{
	char buf[LINE_LEN];
	int inbracket = 0;
	int rtn;
	unsigned i, j;

	/* Copy orig to buf, removing bracketed material */
	pgpAssert (origlen <= sizeof(buf));
	for (i=0,j=0; i<origlen && j < testlen; ++i) {
		if (inbracket && orig[i]=='>')
			inbracket = 0;
		else if (!inbracket && orig[i]=='<')
			inbracket = 1;
		else if (!inbracket)
			buf[j++] = orig[i];
	}
	rtn = memcmp(buf, test, testlen);
	if (!rtn && removelen)
		*removelen = i - j;
	return rtn;
}

/*
 * Contained in buf (of length size) should be a Header-line.  Parse
 * it appropriately, and store it as necessary.
 *
 * A headerline must fit in the armorline buffer (I.e., it must be
 * less that LINE_LEN-1 bytes long), it must start with an Alpha
 * character and then have up to 64 AlphaNumeric characters or dashes,
 * followed by a colon and then a space.  A header that does not
 * conform to this is an error.
 *
 * Returns 0 on success, or a HEADER_ error code.
 */
#define HEADER_TOOLONG	-1
#define HEADER_INVALID	-2
static int
parseHeader (struct Context *ctx, byte const *buf, size_t size)
{
	int i;
	char const *ptr = (char const *)buf;
	static char const messageid[] = "MessageID:";
	char c;

	(void)size;		/* So, why do I have this arg? */

	/* Make sure this is the WHOLE line -- if not, it is an error */
	if (!ctx->eol)
		return HEADER_TOOLONG;

	/* The first character must be an Alpha character */
	if (!isalpha (*ptr++))
		return HEADER_INVALID;

	i = 1;
	for (;;) {
		c = *ptr++;
		/* header ends in a colon */
		if (c == ':')
			break;
		/* Arbitrary label length restriction */
		if (++i > 64)
			return HEADER_TOOLONG;	/* Error */
		/* Following chars much be alphanumeric or '-' */
		if (!isalnum(c) && c != '-')
			return HEADER_INVALID;	/* Error */
	}

	/* Must be a space after the ':' */
	if (*ptr++ != ' ')
		return HEADER_INVALID;

	/*
	 * Now process the header
	 *
	 * ptr points to the value; buf points to the key (of length
	 * (ptr - buf - 2), plus a colon)
	 */
	i = ptr - (char const *)buf - 1;

	/* Process message ID header */
	if (i == sizeof (messageid) - 1 && !memcmp (buf, messageid, i))
		memcpy (ctx->messageid, ptr,
			min ((strlen (ptr) + 1), sizeof (ctx->messageid)));

	/* XXX: signal an unknown header? */

	return 0;
}

/* Write data to be dearmored.
 *
 * The main FSM to control dearmoring.  The actual FSM is kind of
 * complicated so I'll try to explain it by comments in the code.
 * Suffice it to say that there are two main parts of the machine.
 * The first part deals with armor'ed data, and the second part deals
 * with out-of-armored text.  There is a little glue between them, and
 * there you have it...
 *
 * Here are the states, categorized into general phases of activity:
 *
 *	 0			Start state, outside PGP data
 *
 *	 1 -  5		Waiting for start of PGP data
 *
 *	 9 - 10		Process -----BEGIN PGP line
 *
 *	11 - 13		Process headers after -----BEGIN PGP
 *
 *	14 - 16		Setup to begin processing armored data
 *
 *	17 - 20		Process PGP armored data
 *
 *	21 - 23		Process CRC at end of armored data
 *
 *	24 - 27		Process -----END PGP line, finish up, back to 0
 *
 *	30 - 33		Clearsigned message, process Hash: lines if any
 *
 *	34 - 36		Setup to begin processing clearsigned message
 *
 *	39 - 43		Process clearsigned message body, then to 9 for sig
 *
 *	50 - 53		Process binary message, to 26 to finish up when done
 *
 *	60 - 63		Scan MIME headers, get boundary string
 *
 *	65 - 68		Look for initial MIME boundary string, to 34 when found
 *
 *	70 - 72		Check apparent initial MIME separator, to 34 if looks OK
 *
 *	75 - 77		Look for start of PGP sig in MIME clearsign sig subpart, to 9
 *
 *	80 - 83		Look for final MIME clearsign boundary after PGP sig
 *
 */
static size_t
Write (struct PgpPipeline *myself, byte const *buf, size_t size, int *error)
{
	struct Context *ctx;
	struct Message *msg;
	struct MsgPart *part;
	byte *ptr, *num;
	static char const prefix1[] = "-----BEGIN PGP ";
	static char const prefix2[] = ", PART ";
	static char const prefix3[] = "-----END PGP ";
	static char const suffix[] = "-----";	/* With trailing nul */
	static char const signedmsg[] = "SIGNED MESSAGE-----";
	static char const sigprefix[] = "-----BEGIN PGP SIGNATURE-----";
#if MIMEPARSE
	byte *ptr2;
	static char prefix_mimesig2[] = "-----BEGIN PGP MESSAGE-----";
	static char prefix_mime1[] = "content-type: multipart/signed;";
	static char prefix_mime2[] = "protocol=\"application/pgp-signature\"";
	static char prefix_mime3[] = "boundary=";
#if 0
	static char prefix_mime4[] = "micalg=";
	static char prefix_mime5[] = "pgp-";
#endif /* 0 */
#endif /* MIMEPARSE */
	int i, thispart, maxparts, lineused;
	size_t retval, written = 0;
	unsigned inlen;
	size_t bracketlen = 0;
	long crc;
	char temp_c;

	pgpAssert (myself);
	pgpAssert (myself->magic == DEARMORMAGIC);
	pgpAssert (error);

	ctx = (struct Context *)myself->priv;
	pgpAssert (ctx);
	pgpAssert (ctx->tail);

	switch (ctx->state) {
	case 0:
case_0:

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?