Annotation of pgp/src/armor.c, revision 1.1.1.4

1.1.1.2   root        1: /*     armor.c  - ASCII/binary encoding/decoding based partly on PEM RFC1113.
                      2:        PGP: Pretty Good(tm) Privacy - public key cryptography for the masses.
                      3: 
                      4:        (c) Copyright 1990-1992 by Philip Zimmermann.  All rights reserved.
                      5:        The author assumes no liability for damages resulting from the use
                      6:        of this software, even if the damage results from defects in this
                      7:        software.  No warranty is expressed or implied.
                      8: 
                      9:        All the source code Philip Zimmermann wrote for PGP is available for
                     10:        free under the "Copyleft" General Public License from the Free
                     11:        Software Foundation.  A copy of that license agreement is included in
                     12:        the source release package of PGP.  Code developed by others for PGP
                     13:        is also freely available.  Other code that has been incorporated into
                     14:        PGP from other sources was either originally published in the public
                     15:        domain or was used with permission from the various authors.  See the
                     16:        PGP User's Guide for more complete information about licensing,
                     17:        patent restrictions on certain algorithms, trademarks, copyrights,
                     18:        and export controls.  
                     19: */
                     20: 
                     21: #include <ctype.h>
                     22: #include <stdio.h>
                     23: #include <string.h>
                     24: #include "mpilib.h"
                     25: #include "fileio.h"
                     26: #include "mpiio.h"
                     27: #include "language.h"
                     28: #include "pgp.h"
1.1.1.3   root       29: #include "crypto.h"
                     30: #include "armor.h"
                     31: 
                     32: static int dpem_file(char *infile, char *outfile);
                     33: static crcword crchware(byte ch, crcword poly, crcword accum);
                     34: static int pem_file(char *infilename, char *outfilename, char *clearfilename);
                     35: static int pemdecode(FILE *in, FILE *out);
                     36: static void mk_crctbl(crcword poly);
                     37: static boolean is_pemfile(char *infile);
1.1.1.2   root       38: 
                     39: /*     Begin PEM routines.
                     40:        This converts a binary file into printable ASCII characters, in a
                     41:        radix-64 form mostly compatible with the PEM RFC1113 format.
                     42:        This makes it easier to send encrypted files over a 7-bit channel.
                     43: */
                     44: 
                     45: /* Index this array by a 6 bit value to get the character corresponding
                     46:  * to that value.
                     47:  */
1.1.1.3   root       48: static
1.1.1.2   root       49: unsigned char bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ\
                     50: abcdefghijklmnopqrstuvwxyz0123456789+/";
                     51: 
                     52: /* Index this array by a 7 bit value to get the 6-bit binary field
                     53:  * corresponding to that value.  Any illegal characters return high bit set.
                     54:  */
1.1.1.3   root       55: static
1.1.1.2   root       56: unsigned char asctobin[] = {
                     57:        0200,0200,0200,0200,0200,0200,0200,0200,
                     58:        0200,0200,0200,0200,0200,0200,0200,0200,
                     59:        0200,0200,0200,0200,0200,0200,0200,0200,
                     60:        0200,0200,0200,0200,0200,0200,0200,0200,
                     61:        0200,0200,0200,0200,0200,0200,0200,0200,
                     62:        0200,0200,0200,0076,0200,0200,0200,0077,
                     63:        0064,0065,0066,0067,0070,0071,0072,0073,
                     64:        0074,0075,0200,0200,0200,0200,0200,0200,
                     65:        0200,0000,0001,0002,0003,0004,0005,0006,
                     66:        0007,0010,0011,0012,0013,0014,0015,0016,
                     67:        0017,0020,0021,0022,0023,0024,0025,0026,
                     68:        0027,0030,0031,0200,0200,0200,0200,0200,
                     69:        0200,0032,0033,0034,0035,0036,0037,0040,
                     70:        0041,0042,0043,0044,0045,0046,0047,0050,
                     71:        0051,0052,0053,0054,0055,0056,0057,0060,
                     72:        0061,0062,0063,0200,0200,0200,0200,0200
                     73: };
                     74: static long    infile_line;            /* Current line number for mult decodes */
                     75: 
                     76: /************************************************************************/
                     77: 
                     78: /* CRC Routines. */
                     79: /*     These CRC functions are derived from code in chapter 19 of the book 
                     80:        "C Programmer's Guide to Serial Communications", by Joe Campbell.
                     81:        Generalized to any CRC width by Philip Zimmermann.
                     82: */
                     83: 
                     84: #define byte unsigned char
                     85: 
                     86: #define CRCBITS 24     /* may be 16, 24, or 32 */
                     87: /* #define maskcrc(crc) ((crcword)(crc)) */    /* if CRCBITS is 16 or 32 */
                     88: #define maskcrc(crc) ((crc) & 0xffffffL)       /* if CRCBITS is 24 */
                     89: #define CRCHIBIT ((crcword) (1L<<(CRCBITS-1))) /* 0x8000 if CRCBITS is 16 */
                     90: #define CRCSHIFTS (CRCBITS-8)
                     91: 
                     92: /*     Notes on making a good 24-bit CRC--
                     93:        The primitive irreducible polynomial of degree 23 over GF(2),
                     94:        040435651 (octal), comes from Appendix C of "Error Correcting Codes,
                     95:        2nd edition" by Peterson and Weldon, page 490.  This polynomial was
                     96:        chosen for its uniform density of ones and zeros, which has better
                     97:        error detection properties than polynomials with a minimal number of
                     98:        nonzero terms.  Multiplying this primitive degree-23 polynomial by
                     99:        the polynomial x+1 yields the additional property of detecting any
                    100:        odd number of bits in error, which means it adds parity.  This 
                    101:        approach was recommended by Neal Glover.
                    102: 
                    103:        To multiply the polynomial 040435651 by x+1, shift it left 1 bit and
                    104:        bitwise add (xor) the unshifted version back in.  Dropping the unused 
                    105:        upper bit (bit 24) produces a CRC-24 generator bitmask of 041446373 
                    106:        octal, or 0x864cfb hex.  
                    107: 
                    108:        You can detect spurious leading zeros or framing errors in the 
                    109:        message by initializing the CRC accumulator to some agreed-upon 
                    110:        nonzero "random-like" value, but this is a bit nonstandard.  
                    111: */
                    112: 
                    113: #define CCITTCRC 0x1021 /* CCITT's 16-bit CRC generator polynomial */
                    114: #define PRZCRC 0x864cfbL /* PRZ's 24-bit CRC generator polynomial */
                    115: #define CRCINIT 0xB704CEL      /* Init value for CRC accumulator */
                    116: 
1.1.1.3   root      117: static
1.1.1.2   root      118: crcword crctable[256];         /* Table for speeding up CRC's */
                    119: 
                    120: /*     mk_crctbl derives a CRC lookup table from the CRC polynomial.
1.1.1.4 ! root      121:        The table is used later by the crcbytes function given below.
1.1.1.2   root      122:        mk_crctbl only needs to be called once at the dawn of time.
1.1.1.4 ! root      123: 
        !           124:        The theory behind mk_crctbl is that table[i] is initialized
        !           125:        with the CRC of i, and this is related to the CRC of i>>1,
        !           126:        so the CRC of i>>1 (pointed to by p) can be used to derive
        !           127:        the CRC of i (pointed to by q).
1.1.1.2   root      128: */
1.1.1.3   root      129: static
1.1.1.2   root      130: void mk_crctbl(crcword poly)
                    131: {      int i;
1.1.1.4 ! root      132:        crcword t, *p, *q;
        !           133:        p = q = crctable;
        !           134:        *q++ = 0;
        !           135:        *q++ = poly;
        !           136:        for (i = 1; i < 128; i++)
        !           137:        {       t = *++p;
        !           138:                if (t & CRCHIBIT)
        !           139:                {       t <<= 1;
        !           140:                        *q++ = t ^ poly;
        !           141:                        *q++ = t;
        !           142:                }
        !           143:                else
        !           144:                {       t <<= 1;
        !           145:                        *q++ = t;
        !           146:                        *q++ = t ^ poly;
        !           147:                }
        !           148:        }
        !           149: }
1.1.1.2   root      150: 
1.1.1.4 ! root      151: /*
        !           152:  * Accumulate a buffer's worth of bytes into a CRC accumulator,
        !           153:  * returning the new CRC value.
        !           154:  */
        !           155: crcword
        !           156: crcbytes(byte *buf, unsigned len, register crcword accum)
        !           157: {
        !           158:        do {
        !           159:                accum = accum<<8 ^ crctable[(byte)(accum>>CRCSHIFTS) ^ *buf++];
        !           160:        } while (--len);
        !           161:        return maskcrc(accum);
        !           162: } /* crcbytes */
1.1.1.2   root      163: 
                    164: /* Initialize the CRC table using our codes */
1.1.1.3   root      165: void init_crc(void)
1.1.1.2   root      166: {      mk_crctbl(PRZCRC);
                    167: }
                    168: 
                    169: 
                    170: /************************************************************************/
                    171: 
                    172: 
                    173: /* ENC is the basic 1 character encoding function to make a char printing */
                    174: #define ENC(c) ((int)bintoasc[((c) & 077)])
                    175: #define PAD            '='
                    176: 
                    177: /*
                    178:  * output one group of up to 3 bytes, pointed at by p, on file f.
                    179:  * if fewer than 3 are present, the 1 or two extras must be zeros.
                    180:  */
                    181: static void outdec(char *p, FILE *f, int count)
                    182: {
                    183:        int c1, c2, c3, c4;
                    184: 
                    185:        c1 = *p >> 2;
                    186:        c2 = ((*p << 4) & 060) | ((p[1] >> 4) & 017);
                    187:        c3 = ((p[1] << 2) & 074) | ((p[2] >> 6) & 03);
                    188:        c4 = p[2] & 077;
                    189:        putc(ENC(c1), f);
                    190:        putc(ENC(c2), f);
                    191:        if (count == 1)
                    192:        {       putc(PAD, f);
                    193:                putc(PAD, f);
                    194:        }
                    195:        else
                    196:        {       putc(ENC(c3), f);
                    197:                if (count == 2)
                    198:                        putc(PAD, f);
                    199:                else
                    200:                        putc(ENC(c4), f);
                    201:        }
                    202: }      /* outdec */
                    203: 
                    204: 
                    205: /* Output the CRC value, MSB first per normal CRC conventions */
                    206: static void outcrc (word32 crc, FILE *outFile)
                    207: {      /* Output crc */
                    208:        char crcbuf[4];
                    209:        crcbuf[0] = (crc>>16) & 0xff;
                    210:        crcbuf[1] = (crc>>8) & 0xff;
                    211:        crcbuf[2] = (crc>>0) & 0xff;
                    212:        putc(PAD,outFile);
                    213:        outdec (crcbuf,outFile,3);
                    214:        putc('\n',outFile);
                    215: }      /* outcrc */
                    216: 
1.1.1.4 ! root      217: /* Return filename for output (text mode), but replace last letter(s) of
        !           218:  * filename with the ascii for num.  It will use the appropriate number
        !           219:  * of digits for ofnum when converting num, so if ofnum < 10, use 1 digit,
        !           220:  * >= 10 and < 100 use 2 digits, >= 100 and < 1000 use 3 digits.  If its
        !           221:  * >= 1000, then we have other problems to worry about, and this might do
        !           222:  * weird things.
1.1.1.2   root      223:  */
1.1.1.4 ! root      224: static char *numFilename( char *fname, int num, int ofnum)
1.1.1.2   root      225: {      static char     fnamenum[MAX_PATH];
                    226:        int             len;
1.1.1.4 ! root      227:        int             offset = 1;
1.1.1.2   root      228: 
                    229:        strcpy (fnamenum, fname);
                    230:        len = strlen (fnamenum);
1.1.1.4 ! root      231:        do {
        !           232:          fnamenum[len-offset] = '0' + (num%10);
        !           233:          num /= 10;
        !           234:          ofnum /= 10;
        !           235:          offset++;
        !           236:        } while (ofnum >= 1 && offset < 4);
1.1.1.2   root      237:        return(fnamenum);
                    238: }
                    239: 
1.1.1.3   root      240: /*
1.1.1.4 ! root      241:  * Reads and discards a line from the given file.  Returns -1 on error or
        !           242:  * EOF, 0 if the line is blank, and 1 if the line is not blank.
        !           243:  */
        !           244: static int
        !           245: skipline(FILE *f)
        !           246: {
        !           247:        int state, flag, c;
        !           248: 
        !           249:        state = 0;
        !           250:        flag = 0;
        !           251:        for (;;) {
        !           252:                c = getc(f);
        !           253:                if (c == '\n')
        !           254:                        return flag;
        !           255:                if (state)
        !           256:                {       ungetc(c, f);
        !           257:                        return flag;
        !           258:                }
        !           259:                if (c == EOF)
        !           260:                        return -1;
        !           261:                if (c == '\r')
        !           262:                        state = 1;
        !           263:                else if (c != ' ')
        !           264:                        flag = 1;
        !           265:        }
        !           266: } /* skipline */
        !           267: 
        !           268: /*
        !           269:  * Copies a line from the input file to the output.  Does NOT copy the
        !           270:  * trailing newline.  Returns -1 on EOF or error, 0 if the line was terminated
        !           271:  * by EOF, and 1 if the line was terminated with a newline sequence.
        !           272:  */
        !           273: static int
        !           274: copyline(FILE *in, FILE *out)
        !           275: {
        !           276:        int state, flag, c;
        !           277: 
        !           278:        state = 0;
        !           279:        for (;;) {
        !           280:                c = getc(in);
        !           281:                if (c == '\n')
        !           282:                        return 1;
        !           283:                if (state)
        !           284:                {       ungetc(c, in);
        !           285:                        return 1;
        !           286:                }
        !           287:                if (c == EOF)
        !           288:                        return 0;
        !           289:                if (c == '\r')
        !           290:                        state = 1;
        !           291:                else
        !           292:                        putc(c, out);
        !           293:        }
        !           294: } /* copyline */
        !           295: 
        !           296: /*
        !           297:  * Reads a line from file f, up to the size of the buffer.  The line in the
        !           298:  * buffer will NOT include line termination, although any of (CR, LF, CRLF)
        !           299:  * is accepted on input.  The return value is -1 on error, 0 if the line
        !           300:  * was terminated abnormally (EOF, error, or out of buffer space), and
        !           301:  * 1 if the line was terminated normally.
        !           302:  *
        !           303:  * Passing in a buffer less than 2 characters long is not a terribly bright
        !           304:  * idea.
        !           305:  */
        !           306: static int
        !           307: getline(char *buf, int n, FILE *f)
        !           308: {
        !           309:        int state;
        !           310:        char *p;
        !           311:        int c;
        !           312: 
        !           313:        state = 0;
        !           314:        p = buf;
        !           315:        for (;;)
        !           316:        {       c = getc(f);
        !           317:                if (c == '\n')
        !           318:                {       *p = 0;
        !           319:                        return 1;       /* Line terminated with \n or \r\n */
        !           320:                }
        !           321:                if (state)
        !           322:                {       ungetc(c, f);
        !           323:                        *p = 0;
        !           324:                        return 1;       /* Line terminated with \r */
        !           325:                }
        !           326:                if (c == EOF)
        !           327:                {       *p = 0;
        !           328:                        return (p == buf) ? -1 : 0;     /* Error */
        !           329:                }
        !           330:                if (c == '\r')
        !           331:                        state = 1;
        !           332:                else if (--n > 0)
        !           333:                        *p++ = c;
        !           334:                else
        !           335:                {
        !           336:                        ungetc(c, f);
        !           337:                        *p = 0;
        !           338:                        return 0;       /* Out of buffer space */
        !           339:                }
        !           340:        } /* for (;;) */
        !           341: } /* getline */
        !           342: 
        !           343: /*
1.1.1.3   root      344:  * Read a line from file f, buf must be able to hold at least 80 characters.
1.1.1.4 ! root      345:  * Strips trailing spaces and line terminator, can read LF, CRLF and CR
        !           346:  * textfiles.  Anything after 80 characters is ignored.  It can't be ASCII
        !           347:  * armor anyway.
1.1.1.3   root      348:  */
                    349: static char *
                    350: get_armor_line(char *buf, FILE *f)
                    351: {
                    352:        int c, n = 79;
                    353:        char *p = buf;
                    354: 
                    355:        do {
                    356:                c = getc(f);
                    357:                if (c == '\n' || c == '\r' || c == EOF)
                    358:                        break;
                    359:                *p++ = c;
                    360:        } while (--n > 0);
                    361:        if (p == buf && c == EOF)
                    362:        {       *buf = '\0';
                    363:                return NULL;
                    364:        }
                    365:        /* skip to end of line */
                    366:        while (c != '\n' && c != '\r' && c != EOF)
                    367:                c = getc(f);
                    368:        if (c == '\r' && (c = getc(f)) != '\n')
                    369:                ungetc(c, f);
                    370:        while (--p >= buf && *p == ' ')
                    371:                ;
                    372:        *++p = '\0';
                    373:        return buf;
                    374: }
                    375: 
                    376: 
1.1.1.4 ! root      377: /*
        !           378:  * Encode a file in sections.  64 ASCII bytes * 720 lines = 46K, 
        !           379:  * recommended max.  Usenet message size is 50K so this leaves a nice 
        !           380:  * margin for .signature.  In the interests of orthogonality and 
        !           381:  * programmer laziness no check is made for a message containing only 
        !           382:  * a few lines (or even just an 'end') after a section break. 
        !           383:  */
1.1.1.2   root      384: #define LINE_LEN       48L
                    385: int pem_lines = 720;
                    386: #define BYTES_PER_SECTION      (LINE_LEN * pem_lines)
                    387: 
1.1.1.4 ! root      388: #if 1
        !           389: /* This limit is advisory only; longer lines are handled properly.
        !           390:  * The only requirement is that this be at least as long as the longest
        !           391:  * delimiter string used by PGP
        !           392:  * (e.g. "-----BEGIN PGP MESSAGE, PART %02d/%02d-----\n")
        !           393:  */
        !           394: #define MAX_LINE_SIZE 80
        !           395: #else
1.1.1.2   root      396: #ifdef MSDOS   /* limited stack space */
                    397: #define MAX_LINE_SIZE  256
                    398: #else
                    399: #define MAX_LINE_SIZE  1024
                    400: #endif
1.1.1.4 ! root      401: #endif
        !           402: 
        !           403: #ifndef VMS
        !           404: /* armored files are opened in binary mode so that CRLF/LF/CR files
        !           405:    can be handled by all systems */
        !           406: #define        FOPRPEM FOPRBIN
        !           407: #else
        !           408: #define        FOPRPEM FOPRTXT
        !           409: #endif
1.1.1.2   root      410: 
                    411: extern boolean verbose;        /* Undocumented command mode in PGP.C */
                    412: extern boolean filter_mode;
                    413: 
                    414: /*
                    415:  * Copy from infilename to outfilename, PEM encoding as you go along,
                    416:  * and with breaks every
                    417:  * pem_lines lines.
                    418:  * If clearfilename is non-NULL, first output that file preceded by a
                    419:  * special header line.
                    420:  */
1.1.1.3   root      421: static
1.1.1.2   root      422: int pem_file(char *infilename, char *outfilename, char *clearfilename)
                    423: {
                    424:        char buffer[MAX_LINE_SIZE];
1.1.1.4 ! root      425:        int i, rc, bytesRead, lines = 0;
1.1.1.2   root      426:        int noSections, currentSection = 1;
                    427:        long fileLen;
                    428:        crcword crc;
                    429:        FILE *inFile, *outFile, *clearFile;
                    430:        char *blocktype = "MESSAGE";
                    431: 
                    432:        /* open input file as binary */
                    433:        if ((inFile = fopen(infilename,FOPRBIN)) == NULL)
                    434:        {   
                    435:                return(1);
                    436:        }
                    437: 
                    438:        if (!outfilename || pem_lines == 0)
                    439:                noSections = 1;
                    440:        else
                    441:        {       /* Evaluate how many parts this file will comprise */
                    442:                fseek(inFile,0L,SEEK_END);
                    443:                fileLen = ftell(inFile);
                    444:                rewind(inFile);
                    445:                noSections = (fileLen + BYTES_PER_SECTION - 1) / BYTES_PER_SECTION;
1.1.1.3   root      446:                if (noSections > 99)
                    447:                {
                    448:                        pem_lines = ((fileLen+LINE_LEN-1)/LINE_LEN + 98) / 99;
                    449:                        noSections = (fileLen + BYTES_PER_SECTION - 1) / BYTES_PER_SECTION;
                    450:                        fprintf(pgpout, "value for \"armorlines\" is too low, using %d\n", pem_lines);
                    451:                }
1.1.1.2   root      452:        }
                    453:        
                    454:        if (outfilename == NULL)
                    455:                outFile = stdout;
                    456:        else
                    457:        {       if (noSections > 1)
                    458:                {       force_extension(outfilename, ASC_EXTENSION);
1.1.1.4 ! root      459:                        outFile = fopen (numFilename (outfilename, 1, noSections), FOPWTXT);
1.1.1.2   root      460:                }
                    461:                else
                    462:                        outFile = fopen(outfilename,FOPWTXT);
                    463:        }
                    464: 
                    465:        if (outFile == NULL)
                    466:        {       fclose(inFile);
                    467:                return(1);
                    468:        }
                    469: 
                    470:        if (clearfilename)
                    471:        {       if ((clearFile = fopen(clearfilename,FOPRTXT)) == NULL)
                    472:                {       fclose (inFile);
                    473:                        if (outFile != stdout)
                    474:                                fclose (outFile);
                    475:                        return(1);
                    476:                }
                    477:                fprintf (outFile, "-----BEGIN PGP SIGNED MESSAGE-----\n\n");
1.1.1.4 ! root      478:                while ((i = getline(buffer, sizeof buffer, clearFile)) >= 0)
1.1.1.3   root      479:                {
                    480:                        /* Quote lines beginning with '-' as per RFC1113;
1.1.1.4 ! root      481:                         * Also quote lines beginning with "From "; this is
        !           482:                         * for Unix mailers which add ">" to such lines.
1.1.1.3   root      483:                         */
1.1.1.4 ! root      484:                        if (buffer[0] == '-' || strncmp(buffer, "From ", 5)==0)
1.1.1.2   root      485:                                fputs("- ", outFile);
                    486:                        fputs(buffer, outFile);
1.1.1.4 ! root      487:                        /* If there is more on this line, copy it */
        !           488:                        if (i == 0)
        !           489:                                if (copyline(clearFile, outFile) <= 0)
        !           490:                                        break;
        !           491:                        fputc('\n', outFile);
1.1.1.2   root      492:                }
                    493:                fclose (clearFile);
                    494:                putc('\n', outFile);
                    495:                blocktype = "SIGNATURE";
                    496:        }
                    497: 
                    498: 
                    499:        if (noSections == 1)
                    500:        {
                    501:                byte ctb = 0;
                    502:                ctb = getc(inFile);
                    503:                if (is_ctb_type(ctb, CTB_CERT_PUBKEY_TYPE))
                    504:                        blocktype = "PUBLIC KEY BLOCK";
                    505:                fprintf (outFile, "-----BEGIN PGP %s-----\n",blocktype);
                    506:                rewind(inFile);
                    507:        }
                    508:        else
                    509:                fprintf (outFile, "-----BEGIN PGP MESSAGE, PART %02d/%02d-----\n",
                    510:                                        1, noSections);
                    511:        fprintf (outFile, "Version: %s\n",rel_version);
                    512:        fprintf (outFile, "\n");
                    513: 
                    514:        init_crc();
                    515:        crc = CRCINIT;
                    516: 
                    517:        while((bytesRead = fread(buffer,1,LINE_LEN,inFile)) > 0)
                    518:        {       /* Munge up LINE_LEN characters */
                    519:                if (bytesRead < LINE_LEN)
                    520:                        fill0 (buffer+bytesRead, LINE_LEN-bytesRead);
                    521: 
1.1.1.4 ! root      522:                crc = crcbytes((byte *)buffer, bytesRead, crc);
        !           523:                for (i=0; i<bytesRead-3; i+=3)
        !           524:                        outdec(buffer+i, outFile, 3);
        !           525:                outdec(buffer+i, outFile, bytesRead-i);
1.1.1.2   root      526:                putc('\n',outFile);
                    527: 
                    528:                if (++lines == pem_lines && currentSection < noSections)
                    529:                {       lines = 0;
                    530:                        outcrc (crc, outFile);
                    531:                        fprintf(outFile,"-----END PGP MESSAGE, PART %02d/%02d-----\n\n",
                    532:                                currentSection, noSections);
                    533:                        if (write_error(outFile))
                    534:                        {       fclose(outFile);
                    535:                                return(-1);
                    536:                        }
                    537:                        fclose (outFile);
1.1.1.4 ! root      538:                        outFile = fopen (numFilename (outfilename, ++currentSection, noSections), FOPWTXT);
1.1.1.2   root      539:                        if (outFile == NULL)
                    540:                        {       fclose(inFile);
                    541:                                return(-1);
                    542:                        }
                    543:                        fprintf(outFile,"-----BEGIN PGP MESSAGE, PART %02d/%02d-----\n",
                    544:                                        currentSection, noSections);
                    545:                        fprintf(outFile,"\n");
                    546:                        crc = CRCINIT;
                    547:                }
                    548:        }
                    549:        outcrc (crc, outFile);
                    550: 
                    551:        if (noSections == 1)
                    552:                fprintf (outFile, "-----END PGP %s-----\n",blocktype);
                    553:        else
                    554:                fprintf(outFile,"-----END PGP MESSAGE, PART %02d/%02d-----\n",
                    555:                                noSections, noSections);
                    556: 
                    557:        /* Done */
                    558:        fclose(inFile);
                    559:        rc = write_error(outFile);
                    560:        if (outFile == stdout)
                    561:                return rc;
                    562:        fclose(outFile);
                    563: 
                    564:        if (rc)
                    565:                return(-1);
                    566: 
                    567:        if (clearfilename)
                    568:                fprintf (pgpout, PSTR("\nClear signature file: %s\n"), outfilename);
                    569:        else if (noSections == 1)
                    570:                fprintf (pgpout, PSTR("\nTransport armor file: %s\n"), outfilename);
                    571:        else
1.1.1.3   root      572:        {
1.1.1.2   root      573:                fprintf (pgpout, PSTR("\nTransport armor files: "));
                    574:                for (i=1; i<=noSections; ++i)
1.1.1.4 ! root      575:                        fprintf (pgpout, "%s%s", numFilename(outfilename, i, noSections),
1.1.1.2   root      576:                                                i==noSections?"\n":", ");
                    577:        }
                    578:        return(0);
                    579: }      /* pem_file */
                    580: 
                    581: /*     End PEM encode routines. */
                    582: 
1.1.1.3   root      583: 
1.1.1.2   root      584: /*     PEM decode routines.
                    585: */
                    586: 
1.1.1.3   root      587: static
1.1.1.2   root      588: int dpem_buffer(char *inbuf, char *outbuf, int *outlength)
                    589: {
                    590:        unsigned char *bp;
                    591:        int     length;
                    592:        unsigned int c1,c2,c3,c4;
                    593:        register int j;
                    594: 
                    595:        length = 0;
                    596:        bp = (unsigned char *)inbuf;
                    597: 
                    598:        /* FOUR input characters go into each THREE output charcters */
                    599: 
1.1.1.3   root      600:        while (*bp!='\0')
1.1.1.2   root      601:        {       if (*bp&0x80 || (c1=asctobin[*bp])&0x80)
                    602:                        return -1;
                    603:                ++bp;
                    604:                if (*bp&0x80 || (c2=asctobin[*bp])&0x80)
                    605:                        return -1;
                    606:                if (*++bp == PAD)
                    607:                {       c3 = c4 = 0;
                    608:                        length += 1;
1.1.1.4 ! root      609:                        if (c2 & 15)
        !           610:                                return -1;
        !           611:                        if (strcmp((char *)bp, "==") == 0)
        !           612:                                bp += 1;
        !           613:                        else if (strcmp((char *)bp, "=3D=3D") == 0)
        !           614:                                bp += 5;
        !           615:                        else
1.1.1.2   root      616:                                return -1;
                    617:                }
                    618:                else if (*bp&0x80 || (c3=asctobin[*bp])&0x80)
                    619:                                return -1;
                    620:                else
                    621:                {       if (*++bp == PAD)
                    622:                        {       c4 = 0;
                    623:                                length += 2;
1.1.1.4 ! root      624:                                if (c3 & 3)
        !           625:                                        return -1;
        !           626:                                if (strcmp((char *)bp, "=") == 0)
        !           627:                                        ; /* All is well */
        !           628:                                else if (strcmp((char *)bp, "=3D") == 0)
        !           629:                                        bp += 2;
        !           630:                                else
        !           631:                                        return -1;
1.1.1.2   root      632:                        }
                    633:                        else if (*bp&0x80 || (c4=asctobin[*bp])&0x80)
                    634:                                return -1;
                    635:                        else
                    636:                                length += 3;
                    637:                }
                    638:                ++bp;
                    639:                j = (c1 << 2) | (c2 >> 4);
                    640:                *outbuf++=j;
                    641:                j = (c2 << 4) | (c3 >> 2);
                    642:                *outbuf++=j;
                    643:                j = (c3 << 6) | c4;
                    644:                *outbuf++=j;
                    645:        }
                    646: 
                    647:        *outlength = length;
                    648:        return(0);      /* normal return */
                    649: 
                    650: }      /* dpem_buffer */
                    651: 
                    652: static char pemfilename[MAX_PATH];
                    653: /*
                    654:  * try to open the next file of a multi-part armored file
                    655:  * the sequence number is expected at the end of the file name
                    656:  */
                    657: static FILE *
1.1.1.3   root      658: open_next(void)
1.1.1.2   root      659: {
                    660:        char *p, *s, c;
                    661:        FILE *fp;
                    662: 
                    663:        p = pemfilename + strlen(pemfilename);
                    664:        while (--p >= pemfilename && isdigit(*p))
                    665:        {
                    666:                if (*p != '9')
                    667:                {
                    668:                        ++*p;
1.1.1.4 ! root      669:                        return(fopen(pemfilename, FOPRPEM));
1.1.1.2   root      670:                }
                    671:                *p = '0';
                    672:        }
                    673: 
                    674:        /* need an extra digit */
                    675:        if (p >= pemfilename)
                    676:        {       /* try replacing character ( .as0 -> .a10 ) */
                    677:                c = *p;
                    678:                *p = '1';
1.1.1.4 ! root      679:                if ((fp = fopen(pemfilename, FOPRPEM)) != NULL)
1.1.1.2   root      680:                        return(fp);
                    681:                *p = c; /* restore original character */
                    682:        }
                    683:        ++p;
                    684:        for (s = p + strlen(p); s >= p; --s)
                    685:                s[1] = *s;
                    686:        *p = '1'; /* insert digit ( fn0 -> fn10 ) */
                    687: 
1.1.1.4 ! root      688:        return(fopen(pemfilename, FOPRPEM));
1.1.1.2   root      689: }
                    690: 
                    691: /*
                    692:  * Copy from in to out, decoding as you go, with handling for multiple
                    693:  * 500-line blocks of encoded data.
                    694:  */
1.1.1.3   root      695: static
1.1.1.2   root      696: int pemdecode(FILE *in, FILE *out)
                    697: {
1.1.1.3   root      698: char inbuf[80];
1.1.1.2   root      699: char outbuf[80];
                    700: 
                    701: int i, n, status;
                    702: int line;
                    703: int section, currentSection = 1;
                    704: int noSections = 0;
                    705: int gotcrc = 0;
1.1.1.3   root      706: long crc=CRCINIT, chkcrc = -1;
1.1.1.2   root      707: char crcbuf[4];
                    708: int ret_code = 0;
1.1.1.3   root      709: int end_of_message;
1.1.1.2   root      710: 
                    711:        init_crc();
                    712: 
                    713:        for (line = 1; ; line++)        /* for each input line */
                    714:        {
1.1.1.3   root      715:                if (get_armor_line(inbuf, in) == NULL)
                    716:                        end_of_message = 1;
                    717:                else
                    718:                {       end_of_message = (strncmp(inbuf,"-----END PGP MESSAGE,", 21) == 0);
                    719:                        ++infile_line;
1.1.1.2   root      720:                }
                    721: 
1.1.1.3   root      722:                if (currentSection!=noSections && end_of_message)
1.1.1.2   root      723:                {       /* End of this section */
                    724:                        if (gotcrc)
                    725:                        {       if (chkcrc != crc)
                    726:                                {       fprintf(pgpout,PSTR("ERROR: Bad ASCII armor checksum in section %d.\n"), currentSection);
                    727:                                        ret_code = -1;  /* continue with decoding to see if there are other bad parts */
                    728:                                }
                    729:                        }
                    730:                        gotcrc = 0;
                    731:                        crc = CRCINIT;
                    732:                        section = 0;
                    733: 
                    734:                        /* Try and find start of next section */
                    735:                        do
1.1.1.3   root      736:                        {       if (get_armor_line(inbuf,in) == NULL)
1.1.1.2   root      737:                                {       FILE *nextf;
                    738:                                        if ((nextf = open_next()) != NULL)
                    739:                                        {
                    740:                                                fclose(in);
                    741:                                                in = nextf;
                    742:                                                continue;
                    743:                                        }
                    744:                                        fprintf(pgpout,PSTR("Can't find section %d.\n"),currentSection + 1);
                    745:                                        return(-1);
                    746:                                }
                    747:                                ++infile_line;
                    748:                        }
                    749:                        while (strncmp(inbuf,"-----BEGIN PGP MESSAGE",22));
                    750: 
                    751:                        /* Make sure this section is the correct one */
                    752:                        if (2 != sscanf(inbuf,"-----BEGIN PGP MESSAGE, PART %d/%d",
                    753:                                &section,&noSections))
                    754:                        {       fprintf(pgpout,PSTR("Badly formed section header, part %d.\n"),
                    755:                                        currentSection+1);
                    756:                                return(-1);
                    757:                        }
                    758:                        if (section != ++currentSection)
                    759:                        {       fprintf(pgpout,PSTR("Sections out of order, expected part %d"),currentSection);
                    760:                                if (section)
                    761:                                        fprintf(pgpout,PSTR(", got part %d\n"),section);
                    762:                                else
                    763:                                        fputc('\n',pgpout);
                    764:                                return(-1);
                    765:                        }
                    766: 
                    767:                        /* Skip header after BEGIN line */
                    768:                        do {
                    769:                                ++infile_line;
1.1.1.3   root      770:                                if (get_armor_line(inbuf, in) == NULL)
1.1.1.2   root      771:                                {
                    772:                                        fprintf(pgpout,PSTR("ERROR: Hit EOF in header of section %d.\n"),
                    773:                                                currentSection);
                    774:                                        return(-1);
                    775:                                }
1.1.1.3   root      776:                        } while (inbuf[0] != '\0');
1.1.1.2   root      777: 
                    778:                        /* Continue decoding */
                    779:                        continue;
                    780:                }
                    781: 
                    782:                /* Quit when hit the -----END PGP MESSAGE----- line or a blank,
                    783:                        or handle checksum */
                    784:                if (inbuf[0] == PAD)    /* Checksum lines start with PAD char */
1.1.1.4 ! root      785:                {
        !           786:                        /* If the already-armored file is sent through MIME
        !           787:                         * and gets armored again, '=' will become '=3D'.
        !           788:                         * To make life easier, we detect and work around this
        !           789:                         * idiosyncracy.
        !           790:                         */
        !           791:                        if (strlen(inbuf) == 7 &&
        !           792:                            inbuf[1] == '3' && inbuf[2] == 'D')
        !           793:                                status = dpem_buffer(inbuf+3, crcbuf, &n);
        !           794:                        else
        !           795:                                status = dpem_buffer(inbuf+1, crcbuf, &n);
        !           796:                        if ( status < 0 || n != 3 )
1.1.1.2   root      797:                        {       fprintf(pgpout,PSTR("ERROR: Badly formed ASCII armor checksum, line %d.\n"),line);
                    798:                                return -1;
                    799:                        }
                    800:                        chkcrc = (((long)crcbuf[0]<<16)&0xff0000L) +
                    801:                                ((crcbuf[1]<<8)&0xff00L) + (crcbuf[2]&0xffL);
                    802:                        gotcrc = 1;
                    803:                        continue;
                    804:                }
1.1.1.3   root      805:                if (inbuf[0] == '\0')
1.1.1.2   root      806:                {       fprintf(pgpout,PSTR("WARNING: No ASCII armor `END' line.\n"));
                    807:                        break;
                    808:                }
                    809:                if (strncmp(inbuf, "-----END PGP ", 13) == 0)
                    810:                        break;
                    811: 
                    812:                status = dpem_buffer(inbuf,outbuf,&n);
                    813: 
                    814:                if (status == -1)
                    815:                {       fprintf(pgpout,PSTR("ERROR: Bad ASCII armor character, line %d.\n"), line);
                    816:                        gotcrc = 1;     /* this will print part number, continue with next part */
                    817:                        ret_code = -1;
                    818:                }
                    819: 
                    820:                if (n > sizeof outbuf)
                    821:                {       fprintf(pgpout,PSTR("ERROR: Bad ASCII armor line length %d on line %d.\n"),
                    822:                                        n, line);
                    823:                        return -1;
                    824:                }
                    825: 
1.1.1.4 ! root      826:                crc = crcbytes((byte *)outbuf, n, crc);
1.1.1.2   root      827:                if (fwrite(outbuf,1,n,out) != n)
                    828:                {       ret_code = -1;
                    829:                        break;
                    830:                }
                    831: 
                    832:        }       /* line */
                    833: 
                    834:        if (gotcrc)
                    835:        {       if (chkcrc != crc)
                    836:                {       fprintf(pgpout,PSTR("ERROR: Bad ASCII armor checksum"));
                    837:                        if (noSections > 0)
                    838:                                fprintf(pgpout,PSTR(" in section %d"), noSections);
                    839:                        fputc('\n',pgpout);
                    840:                        return -1;
                    841:                }
                    842:        }
                    843:        else
                    844:                fprintf(pgpout,PSTR("Warning: Transport armor lacks a checksum.\n"));
                    845: 
                    846:        return(ret_code);       /* normal return */
                    847: }   /* pemdecode */
                    848: 
                    849: 
1.1.1.3   root      850: static
1.1.1.2   root      851: boolean is_pemfile(char *infile)
                    852: {
                    853:        FILE    *in;
1.1.1.3   root      854:        char    inbuf[80];
1.1.1.2   root      855:        char    outbuf[80];
1.1.1.3   root      856:        int     n, status;
1.1.1.2   root      857:        long    il;
                    858: 
1.1.1.4 ! root      859:        if ((in = fopen(infile, FOPRPEM)) == NULL)
1.1.1.2   root      860:        {       /* can't open file */
                    861:                return(FALSE);
                    862:        }
                    863: 
                    864:        /* Read to infile_line before we begin looking */
                    865:        for (il=0; il<infile_line; ++il)
                    866:        {
1.1.1.3   root      867:                if (get_armor_line(inbuf, in) == NULL)
1.1.1.2   root      868:                {       fclose(in);
                    869:                        return(FALSE);
                    870:                }
                    871:        }
                    872: 
                    873:        /* search file for header line */
                    874:        for (;;)
                    875:        {
1.1.1.3   root      876:                if (get_armor_line(inbuf, in) == NULL)
1.1.1.2   root      877:                        break;
                    878:                else
                    879:                {
                    880:                        if (strncmp(inbuf, "-----BEGIN PGP ", 15) == 0)
                    881:                        {
                    882:                                if (strncmp(inbuf,"-----BEGIN PGP SIGNED MESSAGE-----",34)==0) {
                    883:                                        fclose(in);
                    884:                                        return(TRUE);
                    885:                                }
                    886:                                do {
1.1.1.3   root      887:                                        if (get_armor_line(inbuf, in) == NULL)
1.1.1.2   root      888:                                                break;
                    889: #ifndef STRICT_PEM
                    890:                                        if (dpem_buffer(inbuf,outbuf,&n) == 0 && n == 48)
                    891:                                        {       fclose(in);
                    892:                                                return(TRUE);
                    893:                                        }
                    894: #endif
1.1.1.3   root      895:                                } while (inbuf[0] != '\0');
                    896:                                if (get_armor_line(inbuf, in) == NULL)
1.1.1.2   root      897:                                        break;
                    898:                                status = dpem_buffer(inbuf,outbuf,&n);
                    899:                                if (status < 0)
                    900:                                        break;
                    901:                                fclose(in);
                    902:                                return(TRUE);
                    903:                        }
                    904:                }
                    905:        }
                    906: 
                    907:        fclose(in);
                    908:        return(FALSE);
                    909: 
                    910: }      /* is_pemfile */
                    911: 
                    912: 
1.1.1.3   root      913: static
1.1.1.2   root      914: int dpem_file(char *infile, char *outfile)
                    915: {
                    916: FILE   *in, *out;
                    917: char   buf[MAX_LINE_SIZE];
                    918: char   outbuf[80];
1.1.1.4 ! root      919: int    status, n;
1.1.1.2   root      920: long   il, fpos;
                    921: char   *litfile = NULL;
                    922: 
1.1.1.4 ! root      923:        if ((in = fopen(infile, FOPRPEM)) == NULL)
1.1.1.2   root      924:        {
                    925:                fprintf(pgpout,PSTR("ERROR: Can't find file %s\n"), infile);
                    926:                return(10);
                    927:        }
                    928:        strcpy(pemfilename, infile);    /* store filename for multi-parts */
                    929: 
                    930:        /* Skip to infile_line */
                    931:        for (il=0; il<infile_line; ++il)
                    932:        {
1.1.1.3   root      933:                if (get_armor_line(buf, in) == NULL)
1.1.1.2   root      934:                {       fclose(in);
                    935:                        return -1;
                    936:                }
                    937:        }
                    938: 
                    939:        /* Loop through file, searching for header.  Decode anything with a
                    940:           header, complain if there were no headers. */
                    941: 
                    942:        /* search file for header line */
                    943:     for (;;)
                    944:        {
                    945:                ++infile_line;
1.1.1.3   root      946:                if (get_armor_line(buf, in) == NULL)
1.1.1.2   root      947:                {
                    948:                        fprintf(pgpout,PSTR("ERROR: No ASCII armor `BEGIN' line!\n"));
                    949:                        fclose(in);
                    950:                        return(12);
                    951:                }
                    952:                if (strncmp(buf, "-----BEGIN PGP ", 15) == 0)
                    953:                        break;
                    954:        }
                    955:        if (strncmp(buf, "-----BEGIN PGP SIGNED MESSAGE-----", 34) == 0) {
                    956:                FILE    *litout;
                    957:                char    *canonfile, *p;
1.1.1.4 ! root      958:                int     nline;
1.1.1.2   root      959: 
                    960:                litfile = tempfile(TMP_WIPE|TMP_TMPDIR);
                    961:                if ((litout = fopen (litfile, FOPWTXT)) == NULL)
                    962:                {       fprintf(pgpout,PSTR("\n\007Unable to write ciphertext output file '%s'.\n"), litfile);
                    963:                        fclose(in);
                    964:                        return(-1);
                    965:                }
1.1.1.4 ! root      966:                /* Skip header lines until a blank is hit */
        !           967:                do
        !           968:                {       ++infile_line;
        !           969:                        status = skipline(in);
        !           970:                } while (status != 0);
        !           971:                /* Ignore error; getline will discover it again */
        !           972:                status = 0;
        !           973:                for (;;)
1.1.1.2   root      974:                {       ++infile_line;
1.1.1.4 ! root      975:                        nline = status;
        !           976:                        status = getline(buf, sizeof buf, in);
        !           977:                        if (status < 0)
1.1.1.2   root      978:                        {       fprintf(pgpout,PSTR("ERROR: ASCII armor decode input ended unexpectedly!\n"));
                    979:                                fclose(in);
                    980:                                fclose(litout);
                    981:                                rmtemp (litfile);
                    982:                                return(12);
                    983:                        }
1.1.1.4 ! root      984: 
        !           985:                        if (strncmp(buf,"-----BEGIN PGP ", 15) == 0)
        !           986:                                break;
        !           987:                        if (nline)
        !           988:                                putc('\n', litout);
        !           989:                        /* De-quote lines starting with '- ' */
        !           990:                        fputs(buf + ((buf[0]=='-'&&buf[1]==' ')?2:0), litout);
        !           991:                        /* Copy trailing part of line, if any. */
        !           992:                        if (!status)
        !           993:                                status = copyline(in, litout);
        !           994:                        /* Ignore error; getline will discover it again */
1.1.1.2   root      995:                }
                    996:                fflush (litout);
                    997:                if (ferror(litout))
                    998:                {       fclose(litout);
                    999:                        fclose(in);
                   1000:                        rmtemp (litfile);
                   1001:                        return(-1);
                   1002:                }
                   1003:                fclose (litout);
                   1004:                canonfile = tempfile(TMP_WIPE|TMP_TMPDIR);
1.1.1.3   root     1005:                strip_spaces = TRUE;
1.1.1.2   root     1006:                make_canonical (litfile, canonfile);
                   1007:                rmtemp (litfile);
                   1008:                litfile = canonfile;
                   1009:        }
                   1010: 
                   1011:        /* Skip header after BEGIN line */
                   1012:        do {
                   1013:                ++infile_line;
                   1014:                fpos = ftell(in);
1.1.1.3   root     1015:                if (get_armor_line(buf, in) == NULL)
1.1.1.2   root     1016:                {
                   1017:                        fprintf(pgpout,PSTR("ERROR: Hit EOF in header.\n"));
                   1018:                        fclose(in);
                   1019:                        return(13);
                   1020:                }
                   1021: #ifndef STRICT_PEM
                   1022:                if (dpem_buffer(buf,outbuf,&n) == 0 && n == 48)
                   1023:                {       fseek(in, fpos, SEEK_SET);
                   1024:                        --infile_line;
                   1025:                        break;
                   1026:                }
                   1027: #endif
1.1.1.3   root     1028:        } while (buf[0] != '\0');
1.1.1.2   root     1029: 
                   1030:        if ((out = fopen(outfile, FOPWBIN)) == NULL)
                   1031:        {       fprintf(pgpout,PSTR("\n\007Unable to write ciphertext output file '%s'.\n"), outfile);
                   1032:                fclose(in);
                   1033:                return(-1);
                   1034:        }
                   1035: 
                   1036:        status = pemdecode(in, out);
                   1037: 
                   1038:        if (litfile)
                   1039:        {       /* Glue the literal file read above to the signature */
                   1040:                char lit_mode=MODE_TEXT;        
                   1041:                word32 dummystamp = 0;
                   1042:                FILE *f = fopen(litfile,FOPRBIN);
1.1.1.3   root     1043:                write_ctb_len (out, CTB_LITERAL2_TYPE, fsize(f)+6, FALSE);
1.1.1.2   root     1044:                fwrite ( &lit_mode, 1, 1, out );        /*      write lit_mode */
                   1045:                fputc ('\0', out);                      /* No filename */
                   1046:                fwrite ( &dummystamp, 1, sizeof(dummystamp), out); /* dummy timestamp */
                   1047:                copyfile(f,out,-1L);            /* Append literal file */
                   1048:                fclose (f);
                   1049:                rmtemp(litfile);
                   1050:        }
                   1051: 
                   1052:        if (write_error(out))
                   1053:                status = -1;
                   1054:        fclose(out);
                   1055:        fclose(in);
                   1056:        return(status);
                   1057: }   /* dpem_file */
                   1058: 
                   1059: /* Entry points for generic interface names */
                   1060: int
                   1061: armor_file (char *infile, char *outfile, char *filename, char *clearname)
                   1062: {
                   1063:        if (verbose)
                   1064:                fprintf(pgpout,"armor_file: infile = %s, outfile = %s, filename = %s, clearname = %s\n",
                   1065:                        infile, outfile, filename, clearname);
                   1066:        return pem_file (infile, outfile, clearname);
                   1067: }
                   1068: 
                   1069: int
                   1070: de_armor_file(char *infile, char *outfile, long *curline)
                   1071: {
                   1072:        int status;
                   1073: 
                   1074:        if (verbose)
                   1075:                fprintf(pgpout,"de_armor_file: infile = %s, outfile = %s, curline = %ld\n",
                   1076:                        infile, outfile, *curline);
                   1077:        infile_line = (curline ? *curline : 0);
                   1078:        status = dpem_file (infile, outfile);
                   1079:        if (curline)
                   1080:                *curline = infile_line;
                   1081:        return status;
                   1082: }
                   1083: 
                   1084: boolean
                   1085: is_armor_file (char *infile, long startline)
                   1086: {
                   1087:        infile_line = startline;
                   1088:        return is_pemfile (infile);
                   1089: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.