--- pgp/src/armor.c 2018/04/24 16:38:54 1.1.1.2 +++ pgp/src/armor.c 2018/04/24 16:42:36 1.1.1.6 @@ -1,23 +1,25 @@ /* armor.c - ASCII/binary encoding/decoding based partly on PEM RFC1113. PGP: Pretty Good(tm) Privacy - public key cryptography for the masses. - (c) Copyright 1990-1992 by Philip Zimmermann. All rights reserved. - The author assumes no liability for damages resulting from the use - of this software, even if the damage results from defects in this - software. No warranty is expressed or implied. - - All the source code Philip Zimmermann wrote for PGP is available for - free under the "Copyleft" General Public License from the Free - Software Foundation. A copy of that license agreement is included in - the source release package of PGP. Code developed by others for PGP - is also freely available. Other code that has been incorporated into - PGP from other sources was either originally published in the public - domain or was used with permission from the various authors. See the - PGP User's Guide for more complete information about licensing, - patent restrictions on certain algorithms, trademarks, copyrights, - and export controls. + (c) Copyright 1990-1994 by Philip Zimmermann. All rights reserved. + The author assumes no liability for damages resulting from the use + of this software, even if the damage results from defects in this + software. No warranty is expressed or implied. + + Note that while most PGP source modules bear Philip Zimmermann's + copyright notice, many of them have been revised or entirely written + by contributors who frequently failed to put their names in their + code. Code that has been incorporated into PGP from other authors + was either originally published in the public domain or is used with + permission from the various authors. + + PGP is available for free to the public under certain restrictions. + See the PGP User's Guide (included in the release package) for + important information about licensing, patent restrictions on + certain algorithms, trademarks, copyrights, and export controls. */ + #include #include #include @@ -26,6 +28,15 @@ #include "mpiio.h" #include "language.h" #include "pgp.h" +#include "crypto.h" +#include "armor.h" + +static int dpem_file(char *infile, char *outfile); +static crcword crchware(byte ch, crcword poly, crcword accum); +static int pem_file(char *infilename, char *outfilename, char *clearfilename); +static int pemdecode(FILE *in, FILE *out); +static void mk_crctbl(crcword poly); +static boolean is_pemfile(char *infile); /* Begin PEM routines. This converts a binary file into printable ASCII characters, in a @@ -36,12 +47,14 @@ /* Index this array by a 6 bit value to get the character corresponding * to that value. */ +static unsigned char bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\ abcdefghijklmnopqrstuvwxyz0123456789+/"; /* Index this array by a 7 bit value to get the 6-bit binary field * corresponding to that value. Any illegal characters return high bit set. */ +static unsigned char asctobin[] = { 0200,0200,0200,0200,0200,0200,0200,0200, 0200,0200,0200,0200,0200,0200,0200,0200, @@ -73,89 +86,89 @@ static long infile_line; /* Current lin #define byte unsigned char #define CRCBITS 24 /* may be 16, 24, or 32 */ -/* #define crcword unsigned short */ /* if CRCBITS is 16 */ -#define crcword unsigned long /* if CRCBITS is 24 or 32 */ /* #define maskcrc(crc) ((crcword)(crc)) */ /* if CRCBITS is 16 or 32 */ #define maskcrc(crc) ((crc) & 0xffffffL) /* if CRCBITS is 24 */ #define CRCHIBIT ((crcword) (1L<<(CRCBITS-1))) /* 0x8000 if CRCBITS is 16 */ #define CRCSHIFTS (CRCBITS-8) -/* Notes on making a good 24-bit CRC-- - The primitive irreducible polynomial of degree 23 over GF(2), - 040435651 (octal), comes from Appendix C of "Error Correcting Codes, - 2nd edition" by Peterson and Weldon, page 490. This polynomial was - chosen for its uniform density of ones and zeros, which has better - error detection properties than polynomials with a minimal number of - nonzero terms. Multiplying this primitive degree-23 polynomial by - the polynomial x+1 yields the additional property of detecting any - odd number of bits in error, which means it adds parity. This - approach was recommended by Neal Glover. - - To multiply the polynomial 040435651 by x+1, shift it left 1 bit and - bitwise add (xor) the unshifted version back in. Dropping the unused - upper bit (bit 24) produces a CRC-24 generator bitmask of 041446373 - octal, or 0x864cfb hex. - - You can detect spurious leading zeros or framing errors in the - message by initializing the CRC accumulator to some agreed-upon - nonzero "random-like" value, but this is a bit nonstandard. -*/ +/* + * Notes on making a good 24-bit CRC-- + * The primitive irreducible polynomial of degree 23 over GF(2), + * 040435651 (octal), comes from Appendix C of "Error Correcting Codes, + * 2nd edition" by Peterson and Weldon, page 490. This polynomial was + * chosen for its uniform density of ones and zeros, which has better + * error detection properties than polynomials with a minimal number of + * nonzero terms. Multiplying this primitive degree-23 polynomial by + * the polynomial x+1 yields the additional property of detecting any + * odd number of bits in error, which means it adds parity. This + * approach was recommended by Neal Glover. + * + * To multiply the polynomial 040435651 by x+1, shift it left 1 bit and + * bitwise add (xor) the unshifted version back in. Dropping the unused + * upper bit (bit 24) produces a CRC-24 generator bitmask of 041446373 + * octal, or 0x864cfb hex. + * + * You can detect spurious leading zeros or framing errors in the + * message by initializing the CRC accumulator to some agreed-upon + * nonzero "random-like" value, but this is a bit nonstandard. + */ #define CCITTCRC 0x1021 /* CCITT's 16-bit CRC generator polynomial */ #define PRZCRC 0x864cfbL /* PRZ's 24-bit CRC generator polynomial */ #define CRCINIT 0xB704CEL /* Init value for CRC accumulator */ +static crcword crctable[256]; /* Table for speeding up CRC's */ -/* crchware simulates CRC hardware circuit. Generates true CRC - directly, without requiring extra NULL bytes to be appended - to the message. - Returns new updated CRC accumulator. -*/ -crcword crchware(byte ch, crcword poly, crcword accum) -{ int i; - crcword data; - data = ch; - data <<= CRCSHIFTS; /* shift data to line up with MSB of accum */ - i = 8; /* counts 8 bits of data */ - do - { /* if MSB of (data XOR accum) is TRUE, shift and subtract poly */ - if ((data ^ accum) & CRCHIBIT) - accum = (accum<<1) ^ poly; - else - accum <<= 1; - data <<= 1; - } while (--i); /* counts 8 bits of data */ - return (maskcrc(accum)); -} /* crchware */ - - -/* mk_crctbl derives a CRC lookup table from the CRC polynomial. - The table is used later by crcupdate function given below. - mk_crctbl only needs to be called once at the dawn of time. -*/ +/* + * mk_crctbl derives a CRC lookup table from the CRC polynomial. + * The table is used later by the crcbytes function given below. + * mk_crctbl only needs to be called once at the dawn of time. + * + * The theory behind mk_crctbl is that table[i] is initialized + * with the CRC of i, and this is related to the CRC of i>>1, + * so the CRC of i>>1 (pointed to by p) can be used to derive + * the CRC of i (pointed to by q). + */ +static void mk_crctbl(crcword poly) -{ int i; - for (i=0; i<256; i++) - crctable[i] = crchware((byte) i, poly, 0); -} /* mk_crctbl */ - - -/* crcupdate calculates a CRC using the fast table-lookup method. - Returns new updated CRC accumulator. -*/ -crcword crcupdate(byte data, register crcword accum) -{ byte combined_value; +{ + int i; + crcword t, *p, *q; + p = q = crctable; + *q++ = 0; + *q++ = poly; + for (i = 1; i < 128; i++) { + t = *++p; + if (t & CRCHIBIT) { + t <<= 1; + *q++ = t ^ poly; + *q++ = t; + } else { + t <<= 1; + *q++ = t; + *q++ = t ^ poly; + } + } +} - /* XOR the MSByte of the accum with the data byte */ - combined_value = (accum >> CRCSHIFTS) ^ data; - accum = (accum << 8) ^ crctable[combined_value]; - return (maskcrc(accum)); -} /* crcupdate */ +/* + * Accumulate a buffer's worth of bytes into a CRC accumulator, + * returning the new CRC value. + */ +crcword +crcbytes(byte *buf, unsigned len, register crcword accum) +{ + do { + accum = accum<<8 ^ crctable[(byte)(accum>>CRCSHIFTS) ^ *buf++]; + } while (--len); + return maskcrc(accum); +} /* crcbytes */ /* Initialize the CRC table using our codes */ -void init_crc() -{ mk_crctbl(PRZCRC); +void init_crc(void) +{ + mk_crctbl(PRZCRC); } @@ -180,12 +193,11 @@ static void outdec(char *p, FILE *f, int c4 = p[2] & 077; putc(ENC(c1), f); putc(ENC(c2), f); - if (count == 1) - { putc(PAD, f); + if (count == 1) { putc(PAD, f); - } - else - { putc(ENC(c3), f); + putc(PAD, f); + } else { + putc(ENC(c3), f); if (count == 2) putc(PAD, f); else @@ -196,7 +208,7 @@ static void outdec(char *p, FILE *f, int /* Output the CRC value, MSB first per normal CRC conventions */ static void outcrc (word32 crc, FILE *outFile) -{ /* Output crc */ +{ char crcbuf[4]; crcbuf[0] = (crc>>16) & 0xff; crcbuf[1] = (crc>>8) & 0xff; @@ -206,39 +218,199 @@ static void outcrc (word32 crc, FILE *ou putc('\n',outFile); } /* outcrc */ -/* Return filename for output (text mode), but replace last letter of - * filename with the ascii for num (last two letters if num > 10). +/* Return filename for output (text mode), but replace last letter(s) of + * filename with the ascii for num. It will use the appropriate number + * of digits for ofnum when converting num, so if ofnum < 10, use 1 digit, + * >= 10 and < 100 use 2 digits, >= 100 and < 1000 use 3 digits. If its + * >= 1000, then we have other problems to worry about, and this might do + * weird things. */ -static char *numFilename( char *fname, int num) -{ static char fnamenum[MAX_PATH]; +static char *numFilename( char *fname, int num, int ofnum) +{ + static char fnamenum[MAX_PATH]; int len; + int offset = 1; strcpy (fnamenum, fname); len = strlen (fnamenum); - if (num < 10) - fnamenum[len-1] = '0' + num; - else /* If num > 100, this will be slightly screwy */ - { fnamenum[len-2] = '0' + (num / 10); - fnamenum[len-1] = '0' + (num % 10); + do { + fnamenum[len-offset] = '0' + (num%10); + num /= 10; + ofnum /= 10; + offset++; + } while (ofnum >= 1 && offset < 4); + return fnamenum; +} + +/* + * Reads and discards a line from the given file. Returns -1 on error or + * EOF, 0 if the line is blank, and 1 if the line is not blank. + */ +static int +skipline(FILE *f) +{ + int state, flag, c; + + state = 0; + flag = 0; + for (;;) { + c = getc(f); + if (c == '\n') + return flag; + if (state) { + ungetc(c, f); + return flag; + } + if (c == EOF) + return -1; + if (c == '\r') + state = 1; + else if (c != ' ') + flag = 1; } - return(fnamenum); +} /* skipline */ + +/* + * Copies a line from the input file to the output. Does NOT copy the + * trailing newline. Returns -1 on EOF or error, 0 if the line was terminated + * by EOF, and 1 if the line was terminated with a newline sequence. + */ +static int +copyline(FILE *in, FILE *out) +{ + int state, flag, c; + + state = 0; + for (;;) { + c = getc(in); + if (c == '\n') + return 1; + if (state) { + ungetc(c, in); + return 1; + } + if (c == EOF) + return 0; + if (c == '\r') + state = 1; + else + putc(c, out); + } +} /* copyline */ + +/* + * Reads a line from file f, up to the size of the buffer. The line in the + * buffer will NOT include line termination, although any of (CR, LF, CRLF) + * is accepted on input. The return value is -1 on error, 0 if the line + * was terminated abnormally (EOF, error, or out of buffer space), and + * 1 if the line was terminated normally. + * + * Passing in a buffer less than 2 characters long is not a terribly bright + * idea. + */ +static int +getline(char *buf, int n, FILE *f) +{ + int state; + char *p; + int c; + + state = 0; + p = buf; + for (;;) { + c = getc(f); + if (c == '\n') { + *p = 0; + return 1; /* Line terminated with \n or \r\n */ + } + if (state) { + ungetc(c, f); + *p = 0; + return 1; /* Line terminated with \r */ + } + if (c == EOF) { + *p = 0; + return (p == buf) ? -1 : 0; /* Error */ + } + if (c == '\r') + state = 1; + else if (--n > 0) { + *p++ = c; + } else { + ungetc(c, f); + *p = 0; + return 0; /* Out of buffer space */ + } + } /* for (;;) */ +} /* getline */ + +/* + * Read a line from file f, buf must be able to hold at least 80 characters. + * Strips trailing spaces and line terminator, can read LF, CRLF and CR + * textfiles. Anything after 80 characters is ignored. It can't be ASCII + * armor anyway. + */ +static char * +get_armor_line(char *buf, FILE *f) +{ + int c, n = 79; + char *p = buf; + + do { + c = getc(f); + if (c == '\n' || c == '\r' || c == EOF) + break; + *p++ = c; + } while (--n > 0); + if (p == buf && c == EOF) { + *buf = '\0'; + return NULL; + } + /* skip to end of line */ + while (c != '\n' && c != '\r' && c != EOF) + c = getc(f); + if (c == '\r' && (c = getc(f)) != '\n') + ungetc(c, f); + while (--p >= buf && *p == ' ') + ; + *++p = '\0'; + return buf; } -/* Encode a file in sections. 64 ASCII bytes * 720 lines = 46K, - recommended max. Usenet message size is 50K so this leaves a nice - margin for .signature. In the interests of orthogonality and - programmer laziness no check is made for a message containing only - a few lines (or even just an 'end') after a section break. -*/ + +/* + * Encode a file in sections. 64 ASCII bytes * 720 lines = 46K, + * recommended max. Usenet message size is 50K so this leaves a nice + * margin for .signature. In the interests of orthogonality and + * programmer laziness no check is made for a message containing only + * a few lines (or even just an 'end') after a section break. + */ #define LINE_LEN 48L int pem_lines = 720; #define BYTES_PER_SECTION (LINE_LEN * pem_lines) +#if 1 +/* This limit is advisory only; longer lines are handled properly. + * The only requirement is that this be at least as long as the longest + * delimiter string used by PGP + * (e.g. "-----BEGIN PGP MESSAGE, PART %02d/%02d-----\n") + */ +#define MAX_LINE_SIZE 80 +#else #ifdef MSDOS /* limited stack space */ #define MAX_LINE_SIZE 256 #else #define MAX_LINE_SIZE 1024 #endif +#endif + +#ifndef VMS +/* armored files are opened in binary mode so that CRLF/LF/CR files + can be handled by all systems */ +#define FOPRPEM FOPRBIN +#else +#define FOPRPEM FOPRTXT +#endif extern boolean verbose; /* Undocumented command mode in PGP.C */ extern boolean filter_mode; @@ -250,10 +422,11 @@ extern boolean filter_mode; * If clearfilename is non-NULL, first output that file preceded by a * special header line. */ +static int pem_file(char *infilename, char *outfilename, char *clearfilename) { char buffer[MAX_LINE_SIZE]; - int i,rc,bytesRead,lines = 0; + int i, rc, bytesRead, lines = 0; int noSections, currentSection = 1; long fileLen; crcword crc; @@ -262,49 +435,61 @@ int pem_file(char *infilename, char *out /* open input file as binary */ if ((inFile = fopen(infilename,FOPRBIN)) == NULL) - { - return(1); - } + return 1; - if (!outfilename || pem_lines == 0) + if (!outfilename || pem_lines == 0) { noSections = 1; - else - { /* Evaluate how many parts this file will comprise */ + } else { + /* Evaluate how many parts this file will comprise */ fseek(inFile,0L,SEEK_END); fileLen = ftell(inFile); rewind(inFile); noSections = (fileLen + BYTES_PER_SECTION - 1) / BYTES_PER_SECTION; + if (noSections > 99) { + pem_lines = ((fileLen+LINE_LEN-1)/LINE_LEN + 98) / 99; + noSections = (fileLen + BYTES_PER_SECTION - 1) / BYTES_PER_SECTION; + fprintf(pgpout, "value for \"armorlines\" is too low, using %d\n", pem_lines); + } } - if (outfilename == NULL) + if (outfilename == NULL) { outFile = stdout; - else - { if (noSections > 1) - { force_extension(outfilename, ASC_EXTENSION); - outFile = fopen (numFilename (outfilename, 1), FOPWTXT); - } - else + } else { + if (noSections > 1) { + force_extension(outfilename, ASC_EXTENSION); + outFile = fopen (numFilename (outfilename, 1, noSections), FOPWTXT); + } else { outFile = fopen(outfilename,FOPWTXT); + } } - if (outFile == NULL) - { fclose(inFile); - return(1); + if (outFile == NULL) { + fclose(inFile); + return 1; } - if (clearfilename) - { if ((clearFile = fopen(clearfilename,FOPRTXT)) == NULL) - { fclose (inFile); + if (clearfilename) { + if ((clearFile = fopen(clearfilename,FOPRTXT)) == NULL) { + fclose (inFile); if (outFile != stdout) fclose (outFile); - return(1); + return 1; } fprintf (outFile, "-----BEGIN PGP SIGNED MESSAGE-----\n\n"); - while (fgets(buffer, sizeof(buffer), clearFile) != NULL) - { /* Quote lines beginning with '-' */ - if (buffer[0] == '-') + while ((i = getline(buffer, sizeof buffer, clearFile)) >= 0) + { + /* Quote lines beginning with '-' as per RFC1113; + * Also quote lines beginning with "From "; this is + * for Unix mailers which add ">" to such lines. + */ + if (buffer[0] == '-' || strncmp(buffer, "From ", 5)==0) fputs("- ", outFile); fputs(buffer, outFile); + /* If there is more on this line, copy it */ + if (i == 0) + if (copyline(clearFile, outFile) <= 0) + break; + fputc('\n', outFile); } fclose (clearFile); putc('\n', outFile); @@ -312,57 +497,50 @@ int pem_file(char *infilename, char *out } - if (noSections == 1) - { + if (noSections == 1) { byte ctb = 0; ctb = getc(inFile); if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE)) blocktype = "PUBLIC KEY BLOCK"; fprintf (outFile, "-----BEGIN PGP %s-----\n",blocktype); rewind(inFile); - } - else + } else { fprintf (outFile, "-----BEGIN PGP MESSAGE, PART %02d/%02d-----\n", 1, noSections); - fprintf (outFile, "Version: %s\n",rel_version); + } + fprintf (outFile, "Version: %s\n",LANG (rel_version)); + if (globalCommentString[0]) + fprintf (outFile, "Comment: %s\n", globalCommentString); fprintf (outFile, "\n"); init_crc(); crc = CRCINIT; - while((bytesRead = fread(buffer,1,LINE_LEN,inFile)) > 0) - { /* Munge up LINE_LEN characters */ + while((bytesRead = fread(buffer,1,LINE_LEN,inFile)) > 0) { + /* Munge up LINE_LEN characters */ if (bytesRead < LINE_LEN) fill0 (buffer+bytesRead, LINE_LEN-bytesRead); - for (i=0; i= buf && (*bp == ' ' || *bp == '\r')) - --bp; - bp[1] = '\n'; /* Terminate line cleanly */ - bp[2] = '\0'; -} - - +static int dpem_buffer(char *inbuf, char *outbuf, int *outlength) { unsigned char *bp; @@ -436,35 +593,47 @@ int dpem_buffer(char *inbuf, char *outbu unsigned int c1,c2,c3,c4; register int j; - strip_trailing(inbuf); length = 0; bp = (unsigned char *)inbuf; /* FOUR input characters go into each THREE output charcters */ - while (*bp!='\0' && *bp!='\n' && *bp!='\r') - { if (*bp&0x80 || (c1=asctobin[*bp])&0x80) + while (*bp != '\0') { + if (*bp&0x80 || (c1=asctobin[*bp])&0x80) return -1; ++bp; if (*bp&0x80 || (c2=asctobin[*bp])&0x80) return -1; - if (*++bp == PAD) - { c3 = c4 = 0; + if (*++bp == PAD) { + c3 = c4 = 0; length += 1; - if (*++bp != PAD) + if (c2 & 15) return -1; - } - else if (*bp&0x80 || (c3=asctobin[*bp])&0x80) + if (strcmp((char *)bp, "==") == 0) + bp += 1; + else if (strcmp((char *)bp, "=3D=3D") == 0) + bp += 5; + else return -1; - else - { if (*++bp == PAD) - { c4 = 0; + } else if (*bp&0x80 || (c3=asctobin[*bp])&0x80) { + return -1; + } else { + if (*++bp == PAD) { + c4 = 0; length += 2; - } - else if (*bp&0x80 || (c4=asctobin[*bp])&0x80) + if (c3 & 3) + return -1; + if (strcmp((char *)bp, "=") == 0) + ; /* All is well */ + else if (strcmp((char *)bp, "=3D") == 0) + bp += 2; + else + return -1; + } else if (*bp&0x80 || (c4=asctobin[*bp])&0x80) { return -1; - else + } else { length += 3; + } } ++bp; j = (c1 << 2) | (c2 >> 4); @@ -476,7 +645,7 @@ int dpem_buffer(char *inbuf, char *outbu } *outlength = length; - return(0); /* normal return */ + return 0; /* normal return */ } /* dpem_buffer */ @@ -486,29 +655,27 @@ static char pemfilename[MAX_PATH]; * the sequence number is expected at the end of the file name */ static FILE * -open_next() +open_next(void) { char *p, *s, c; FILE *fp; p = pemfilename + strlen(pemfilename); - while (--p >= pemfilename && isdigit(*p)) - { - if (*p != '9') - { + while (--p >= pemfilename && isdigit(*p)) { + if (*p != '9') { ++*p; - return(fopen(pemfilename, FOPRTXT)); + return fopen(pemfilename, FOPRPEM); } *p = '0'; } /* need an extra digit */ - if (p >= pemfilename) - { /* try replacing character ( .as0 -> .a10 ) */ + if (p >= pemfilename) { + /* try replacing character ( .as0 -> .a10 ) */ c = *p; *p = '1'; - if ((fp = fopen(pemfilename, FOPRTXT)) != NULL) - return(fp); + if ((fp = fopen(pemfilename, FOPRPEM)) != NULL) + return fp; *p = c; /* restore original character */ } ++p; @@ -516,44 +683,44 @@ open_next() s[1] = *s; *p = '1'; /* insert digit ( fn0 -> fn10 ) */ - return(fopen(pemfilename, FOPRTXT)); + return fopen(pemfilename, FOPRPEM); } /* * Copy from in to out, decoding as you go, with handling for multiple * 500-line blocks of encoded data. */ +static int pemdecode(FILE *in, FILE *out) { -char inbuf[MAX_LINE_SIZE]; -char outbuf[80]; + char inbuf[80]; + char outbuf[80]; -int i, n, status; -int line; -int section, currentSection = 1; -int noSections = 0; -int gotcrc = 0; -long crc=CRCINIT, chkcrc; -char crcbuf[4]; -int ret_code = 0; + int i, n, status; + int line; + int section, currentSection = 1; + int noSections = 0; + int gotcrc = 0; + long crc=CRCINIT, chkcrc = -1; + char crcbuf[4]; + int ret_code = 0; + int end_of_message; init_crc(); - for (line = 1; ; line++) /* for each input line */ - { - if (fgets(inbuf, sizeof(inbuf), in) == NULL) - { - fprintf(pgpout,PSTR("ERROR: ASCII armor decode input ended unexpectedly!\n")); - return(18); + for (line = 1; ; line++) { /* for each input line */ + if (get_armor_line(inbuf, in) == NULL) { + end_of_message = 1; + } else { + end_of_message = (strncmp(inbuf,"-----END PGP MESSAGE,", 21) == 0); + ++infile_line; } - ++infile_line; - if (currentSection!=noSections && - strncmp(inbuf,"-----END PGP MESSAGE,", 21) == 0) - { /* End of this section */ - if (gotcrc) - { if (chkcrc != crc) - { fprintf(pgpout,PSTR("ERROR: Bad ASCII armor checksum in section %d.\n"), currentSection); + if (currentSection!=noSections && end_of_message) { + /* End of this section */ + if (gotcrc) { + if (chkcrc != crc) { + fprintf(pgpout,LANG("ERROR: Bad ASCII armor checksum in section %d.\n"), currentSection); ret_code = -1; /* continue with decoding to see if there are other bad parts */ } } @@ -562,17 +729,16 @@ int ret_code = 0; section = 0; /* Try and find start of next section */ - do - { if (fgets(inbuf,sizeof(inbuf),in) == NULL) - { FILE *nextf; - if ((nextf = open_next()) != NULL) - { + do { + if (get_armor_line(inbuf,in) == NULL) { + FILE *nextf; + if ((nextf = open_next()) != NULL) { fclose(in); in = nextf; continue; } - fprintf(pgpout,PSTR("Can't find section %d.\n"),currentSection + 1); - return(-1); + fprintf(pgpout,LANG("Can't find section %d.\n"),currentSection + 1); + return -1; } ++infile_line; } @@ -581,29 +747,29 @@ int ret_code = 0; /* Make sure this section is the correct one */ if (2 != sscanf(inbuf,"-----BEGIN PGP MESSAGE, PART %d/%d", §ion,&noSections)) - { fprintf(pgpout,PSTR("Badly formed section header, part %d.\n"), + { + fprintf(pgpout,LANG("Badly formed section header, part %d.\n"), currentSection+1); - return(-1); + return -1; } - if (section != ++currentSection) - { fprintf(pgpout,PSTR("Sections out of order, expected part %d"),currentSection); + if (section != ++currentSection) { + fprintf(pgpout,LANG("Sections out of order, expected part %d"),currentSection); if (section) - fprintf(pgpout,PSTR(", got part %d\n"),section); + fprintf(pgpout,LANG(", got part %d\n"),section); else fputc('\n',pgpout); - return(-1); + return -1; } /* Skip header after BEGIN line */ do { ++infile_line; - if (fgets(inbuf, sizeof inbuf, in) == NULL) - { - fprintf(pgpout,PSTR("ERROR: Hit EOF in header of section %d.\n"), + if (get_armor_line(inbuf, in) == NULL) { + fprintf(pgpout,LANG("ERROR: Hit EOF in header of section %d.\n"), currentSection); - return(-1); + return -1; } - } while (inbuf[0] != '\n' && inbuf[0] != '\r'); + } while (inbuf[0] != '\0'); /* Continue decoding */ continue; @@ -611,10 +777,19 @@ int ret_code = 0; /* Quit when hit the -----END PGP MESSAGE----- line or a blank, or handle checksum */ - if (inbuf[0] == PAD) /* Checksum lines start with PAD char */ - { status = dpem_buffer (inbuf+1,crcbuf,&n); - if (status==-1 || n!=3) - { fprintf(pgpout,PSTR("ERROR: Badly formed ASCII armor checksum, line %d.\n"),line); + if (inbuf[0] == PAD) { /* Checksum lines start with PAD char */ + /* If the already-armored file is sent through MIME + * and gets armored again, '=' will become '=3D'. + * To make life easier, we detect and work around this + * idiosyncracy. + */ + if (strlen(inbuf) == 7 && + inbuf[1] == '3' && inbuf[2] == 'D') + status = dpem_buffer(inbuf+3, crcbuf, &n); + else + status = dpem_buffer(inbuf+1, crcbuf, &n); + if ( status < 0 || n != 3 ) { + fprintf(pgpout,LANG("ERROR: Badly formed ASCII armor checksum, line %d.\n"),line); return -1; } chkcrc = (((long)crcbuf[0]<<16)&0xff0000L) + @@ -622,8 +797,8 @@ int ret_code = 0; gotcrc = 1; continue; } - if (inbuf[0] == '\n' || inbuf[0] == '\r') - { fprintf(pgpout,PSTR("WARNING: No ASCII armor `END' line.\n")); + if (inbuf[0] == '\0') { + fprintf(pgpout,LANG("WARNING: No ASCII armor `END' line.\n")); break; } if (strncmp(inbuf, "-----END PGP ", 13) == 0) @@ -631,127 +806,122 @@ int ret_code = 0; status = dpem_buffer(inbuf,outbuf,&n); - if (status == -1) - { fprintf(pgpout,PSTR("ERROR: Bad ASCII armor character, line %d.\n"), line); + if (status == -1) { + fprintf(pgpout,LANG("ERROR: Bad ASCII armor character, line %d.\n"), line); gotcrc = 1; /* this will print part number, continue with next part */ ret_code = -1; } - if (n > sizeof outbuf) - { fprintf(pgpout,PSTR("ERROR: Bad ASCII armor line length %d on line %d.\n"), + if (n > sizeof outbuf) { + fprintf(pgpout,LANG("ERROR: Bad ASCII armor line length %d on line %d.\n"), n, line); return -1; } - for (i=0; i 0) - fprintf(pgpout,PSTR(" in section %d"), noSections); + fprintf(pgpout,LANG(" in section %d"), noSections); fputc('\n',pgpout); return -1; } + } else { + fprintf(pgpout,LANG("Warning: Transport armor lacks a checksum.\n")); } - else - fprintf(pgpout,PSTR("Warning: Transport armor lacks a checksum.\n")); - return(ret_code); /* normal return */ + return ret_code; /* normal return */ } /* pemdecode */ +static boolean is_pemfile(char *infile) { FILE *in; - char inbuf[MAX_LINE_SIZE]; + char inbuf[80]; char outbuf[80]; - int i, n, status; + int n, status; long il; - if ((in = fopen(infile, FOPRTXT)) == NULL) - { /* can't open file */ - return(FALSE); + if ((in = fopen(infile, FOPRPEM)) == NULL) { + /* can't open file */ + return FALSE; } /* Read to infile_line before we begin looking */ - for (il=0; il= buf && *p == '\n') - { nline = TRUE; - /* remove \n, original file may have ended in unterminated line */ - *p = '\0'; - } - /* De-quote lines starting with '- ' */ - fputs(buf + ((buf[0]=='-'&&buf[1]==' ')?2:0), litout); + return 12; } + + if (strncmp(buf,"-----BEGIN PGP ", 15) == 0) + break; + if (nline) + putc('\n', litout); + /* De-quote lines starting with '- ' */ + fputs(buf + ((buf[0]=='-'&&buf[1]==' ')?2:0), litout); + /* Copy trailing part of line, if any. */ + if (!status) + status = copyline(in, litout); + /* Ignore error; getline will discover it again */ } fflush (litout); - if (ferror(litout)) - { fclose(litout); + if (ferror(litout)) { + fclose(litout); fclose(in); rmtemp (litfile); - return(-1); + return -1; } fclose (litout); canonfile = tempfile(TMP_WIPE|TMP_TMPDIR); + strip_spaces = TRUE; make_canonical (litfile, canonfile); rmtemp (litfile); litfile = canonfile; @@ -831,35 +1000,34 @@ char *litfile = NULL; do { ++infile_line; fpos = ftell(in); - if (fgets(buf, sizeof buf, in) == NULL) - { - fprintf(pgpout,PSTR("ERROR: Hit EOF in header.\n")); + if (get_armor_line(buf, in) == NULL) { + fprintf(pgpout,LANG("ERROR: Hit EOF in header.\n")); fclose(in); - return(13); + return 13; } #ifndef STRICT_PEM - if (dpem_buffer(buf,outbuf,&n) == 0 && n == 48) - { fseek(in, fpos, SEEK_SET); + if (dpem_buffer(buf,outbuf,&n) == 0 && n == 48) { + fseek(in, fpos, SEEK_SET); --infile_line; break; } #endif - } while (buf[0] != '\n'); + } while (buf[0] != '\0'); - if ((out = fopen(outfile, FOPWBIN)) == NULL) - { fprintf(pgpout,PSTR("\n\007Unable to write ciphertext output file '%s'.\n"), outfile); + if ((out = fopen(outfile, FOPWBIN)) == NULL) { + fprintf(pgpout,LANG("\n\007Unable to write ciphertext output file '%s'.\n"), outfile); fclose(in); - return(-1); + return -1; } status = pemdecode(in, out); - if (litfile) - { /* Glue the literal file read above to the signature */ + if (litfile) { + /* Glue the literal file read above to the signature */ char lit_mode=MODE_TEXT; word32 dummystamp = 0; FILE *f = fopen(litfile,FOPRBIN); - write_ctb_len (out, CTB_LITERAL_TYPE, fsize(f), FALSE); + write_ctb_len (out, CTB_LITERAL2_TYPE, fsize(f)+6, FALSE); fwrite ( &lit_mode, 1, 1, out ); /* write lit_mode */ fputc ('\0', out); /* No filename */ fwrite ( &dummystamp, 1, sizeof(dummystamp), out); /* dummy timestamp */ @@ -872,7 +1040,7 @@ char *litfile = NULL; status = -1; fclose(out); fclose(in); - return(status); + return status; } /* dpem_file */ /* Entry points for generic interface names */