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