--- pgp/src/crypto.c 2018/04/24 16:37:53 1.1 +++ pgp/src/crypto.c 2018/04/24 16:41:18 1.1.1.5 @@ -1,24 +1,40 @@ /* crypto.c - Cryptographic routines for PGP. PGP: Pretty Good(tm) Privacy - public key cryptography for the masses. - (c) Copyright 1990-1992 by Philip Zimmermann. All rights reserved. + (c) Copyright 1990-1994 by Philip Zimmermann. All rights reserved. The author assumes no liability for damages resulting from the use of this software, even if the damage results from defects in this software. No warranty is expressed or implied. - All the source code Philip Zimmermann wrote for PGP is available for - free under the "Copyleft" General Public License from the Free - Software Foundation. A copy of that license agreement is included in - the source release package of PGP. Code developed by others for PGP - is also freely available. Other code that has been incorporated into - PGP from other sources was either originally published in the public - domain or was used with permission from the various authors. See the - PGP User's Guide for more complete information about licensing, - patent restrictions on certain algorithms, trademarks, copyrights, - and export controls. + Note that while most PGP source modules bear Philip Zimmermann's + copyright notice, many of them have been revised or entirely written + by contributors who frequently failed to put their names in their + code. Code that has been incorporated into PGP from other authors + was either originally published in the public domain or is used with + permission from the various authors. + + PGP is available for free to the public under certain restrictions. + See the PGP User's Guide (included in the release package) for + important information about licensing, patent restrictions on + certain algorithms, trademarks, copyrights, and export controls. + + Modified: 12-Nov-92 HAJK + Add FDL stuff for VAX/VMS local mode. + Reopen temporary files rather than create new version. + + Modified: 13-Dec-92 Derek Atkins +#include #include #include #include @@ -26,22 +42,37 @@ #include "mpilib.h" #include "mpiio.h" #include "random.h" -#include "idea.h" #include "crypto.h" #include "keymgmt.h" +#include "keymaint.h" #include "mdfile.h" #include "fileio.h" +#include "charset.h" #include "language.h" #include "pgp.h" +#include "exitpgp.h" +#include "zipup.h" +#include "rsaglue.h" +#include "idea.h" #define ENCRYPT_IT FALSE /* to pass to idea_file */ #define DECRYPT_IT TRUE /* to pass to idea_file */ -/* The kbhit() function: Determines if a key has been hit. May not be - available in some implementations */ +#define USE_LITERAL2 + + +/* This variable stores the md5 hash of the current file, if it is + available. It is used in make_random_ideakey. */ +static unsigned char md5buf[16]; + +/* This flag is set if the buffer above has been filled. */ +static char already_have_md5 = 0; + + +/* Used by encryptfile */ +static int encryptkeyintofile(FILE *g, char *mcguffin, byte *keybuf, + char *keyfile, int ckp_length, int keys_used); -int kbhit( void ); -int zipup(FILE *, FILE *); #ifdef M_XENIX long time(); #endif @@ -50,7 +81,8 @@ long time(); void CToPascal(char *s) -{ /* "xyz\0" --> "\3xyz" ... converts C string to Pascal string */ +{ + /* "xyz\0" --> "\3xyz" ... converts C string to Pascal string */ int i,j; j = string_length(s); for (i=j; i!=0; i--) @@ -60,7 +92,8 @@ void CToPascal(char *s) void PascalToC( char *s ) -{ /* "\3xyz" --> "xyz\0" ... converts Pascal string to C string */ +{ + /* "\3xyz" --> "xyz\0" ... converts Pascal string to C string */ int i,j; for (i=0,j=((byte *) s)[0]; i= 0) - { f = fopen(keyfile, "rb"); + if (getpubuserid (keyfile, fp, (byte *) userid, &fpusr, &usrpktlen, FALSE) >= 0) + { + f = fopen(keyfile, FOPRBIN); fseek (f, fpusr+usrpktlen, SEEK_SET); /* Read trust byte */ trust_status = read_trust(f, &keyctrl); fseek(f, fp, SEEK_SET); - if (is_compromised(f)) - { + if (is_compromised(f)) { CToPascal(userid); fprintf(pgpout, "\n"); show_key(f, fp, 0); fclose (f); - fprintf(pgpout, PSTR("\007\nWARNING: This key has been revoked by its owner,\n\ + fprintf(pgpout, LANG("\007\nWARNING: This key has been revoked by its owner,\n\ possibly because the secret key was compromised.\n")); - if (warn_only) - { /* this is only for checking signatures */ - fprintf(pgpout, PSTR("This could mean that this signature is a forgery.\n")); - return(1); - } - else - { /* don't use it for encryption */ - fprintf(pgpout, PSTR("You cannot use this revoked key.\n")); - return(-1); + if (warn_only) { + /* this is only for checking signatures */ + fprintf(pgpout, LANG("This could mean that this signature is a forgery.\n")); + return 1; + } else { /* don't use it for encryption */ + fprintf(pgpout, LANG("You cannot use this revoked key.\n")); + return -1; } } fclose (f); } CToPascal(userid); - if ((keyctrl & KC_LEGIT_MASK) != KC_LEGIT_COMPLETE) - { byte userid0[256]; + if ((keyctrl & KC_LEGIT_MASK) != KC_LEGIT_COMPLETE) { + byte userid0[256]; PascalToC(userid); strcpy ((char *) userid0, userid); CToPascal(userid); + if ((keyctrl & KC_LEGIT_MASK) == KC_LEGIT_UNKNOWN) - fprintf(pgpout,PSTR("\007\nWARNING: Because this public key is not certified with a trusted\n\ + fprintf(pgpout,LANG("\007\nWARNING: Because this public key is not certified with a trusted\n\ signature, it is not known with high confidence that this public key\n\ -actually belongs to: \"%s\".\n"), EXTERNAL((char *)userid0)); +actually belongs to: \"%s\".\n"), LOCAL_CHARSET((char *)userid0)); + if ((keyctrl & KC_LEGIT_MASK) == KC_LEGIT_UNTRUSTED) - fprintf(pgpout, PSTR("\007\nWARNING: This public key is not trusted to actually belong to:\n\ -\"%s\".\n"), EXTERNAL((char *)userid0)); + fprintf(pgpout, LANG("\007\nWARNING: This public key is not trusted to actually belong to:\n\ +\"%s\".\n"), LOCAL_CHARSET((char *)userid0)); + if ((keyctrl & KC_LEGIT_MASK) == KC_LEGIT_MARGINAL) - fprintf(pgpout,PSTR("\007\nWARNING: Because this public key is not certified with enough trusted\n\ + fprintf(pgpout,LANG("\007\nWARNING: Because this public key is not certified with enough trusted\n\ signatures, it is not known with high confidence that this public key\n\ -actually belongs to: \"%s\".\n"), EXTERNAL((char *)userid0)); - if (!filter_mode && !warn_only && !(keyctrl & KC_WARNONLY)) - { fprintf(pgpout,PSTR("\nAre you sure you want to use this public key (y/N)? ")); +actually belongs to: \"%s\".\n"), LOCAL_CHARSET((char *)userid0)); + + if (keyctrl & KC_WARNONLY) { + /* KC_WARNONLY bit already set, user must have approved before. */ + fprintf(pgpout, LANG("But you previously approved using this public key anyway.\n")); + } + + if (!filter_mode && !batchmode && !warn_only && !(keyctrl & KC_WARNONLY)) + { + fprintf(pgpout,LANG("\nAre you sure you want to use this public key (y/N)? ")); if (!getyesno('n')) - return(-1); - if (trust_status == 0 && (f = fopen(keyfile, "r+b")) != NULL) - { fseek (f, fpusr+usrpktlen, SEEK_SET); + return -1; + if (trust_status == 0 && (f = fopen(keyfile, FOPRWBIN)) != NULL) + { + fseek (f, fpusr+usrpktlen, SEEK_SET); keyctrl |= KC_WARNONLY; write_trust(f, keyctrl); fclose(f); } } } - return(0); + return 0; } /* warn_signatures */ boolean legal_ctb(byte ctb) -{ /* Used to determine if nesting should be allowed. */ +/* Used to determine if nesting should be allowed. */ +{ boolean legal; byte ctbtype; if (!is_ctb(ctb)) /* not even a bonafide CTB */ - return(FALSE); + return FALSE; /* Sure hope CTB internal bit definitions don't change... */ ctbtype = (ctb & CTB_TYPE_MASK) >> 2; /* Only allow these CTB types to be nested... */ - legal = ( - (ctbtype==CTB_PKE_TYPE) - || (ctbtype==CTB_SKE_TYPE) - || (ctbtype==CTB_CERT_SECKEY_TYPE) - || (ctbtype==CTB_CERT_PUBKEY_TYPE) - || (ctbtype==CTB_LITERAL_TYPE) - || (ctbtype==CTB_COMPRESSED_TYPE) - || (ctbtype==CTB_CKE_TYPE) + legal = ( (ctbtype==CTB_PKE_TYPE) + || (ctbtype==CTB_SKE_TYPE) + || (ctbtype==CTB_CERT_SECKEY_TYPE) + || (ctbtype==CTB_CERT_PUBKEY_TYPE) + || (ctbtype==CTB_LITERAL_TYPE) + || (ctbtype==CTB_LITERAL2_TYPE) + || (ctbtype==CTB_COMPRESSED_TYPE) + || (ctbtype==CTB_CKE_TYPE) ); - return(legal); + return legal; } /* legal_ctb */ @@ -274,125 +321,81 @@ boolean legal_ctb(byte ctb) */ int version_error(int val, int checkval) -{ if (val != checkval) - { fprintf (pgpout, PSTR( -"\n\007Unsupported packet format - you need a newer version of PGP for this file.\n")); - return(1); +{ + if (val != checkval) { + fprintf (pgpout, LANG("\n\007Unsupported packet format - you need a newer version of PGP for this file.\n")); + return 1; } - return(0); + return 0; } /*-------------------------------------------------------------------------*/ +#define RAND_PREFIX_LENGTH 8 /* Length of IV for IDEA encryption */ -int strong_pseudorandom(byte *buf, int bufsize) -/* - Reads IDEA random key and random number seed from file, cranks the - the seed through the idearand strong pseudorandom number generator, - and writes them back out. This is used for generation of - cryptographically strong pseudorandom numbers. This is mainly to - save the user the trouble of having to type in lengthy keyboard - sequences for generation of truly random numbers every time we want - to make a random session key. This pseudorandom generator will only - work if the file containing the random seed exists and is not empty. - If it doesn't exist, it will be automatically created. If it exists - and is empty or nearly empty, it will not be used. -*/ -{ char seedfile[MAX_PATH]; /* Random seed filename */ - FILE *f; - byte key[IDEAKEYSIZE]; - byte seed[IDEABLOCKSIZE]; - int i; - word32 tstamp; byte *timestamp = (byte *) &tstamp; - - buildfilename(seedfile,RANDSEED_FILENAME); - - if (!file_exists(seedfile)) /* No seed file. Start one... */ - { f = fopen(seedfile,"wb"); /* open for writing binary */ - if (f==NULL) /* failed to create seedfile */ - return(-1); /* error: no random number seed file available */ - fclose(f); /* close new empty seed file */ - /* kickstart the generator with true random numbers */ - fprintf(pgpout,PSTR("Initializing random seed file...")); - randaccum(8*(sizeof(key)+sizeof(seed))); - for (i=1; i 0xFFFFL)) - { llength = 4; + if (big || (length > 0xFFFFL)) { + llength = 4; llenb = 2; - } - else if ((word16)length > 0xFF) - { llength = 2; + } else if ((word16)length > 0xFF) { + llength = 2; llenb = 1; - } - else - { llength = 1; + } else { + llength = 1; llenb = 0; } ctb = CTB_BYTE(ctb_type, llenb); @@ -452,67 +452,72 @@ void write_ctb_len (FILE *f, byte ctb_ty } /* write_ctb_len */ +static int idea_file(byte *ideakey, boolean decryp, FILE *f, FILE *g, word32 lenfile) -/* Use IDEA in cipher feedback (CFB) mode to encrypt or decrypt a file. - The encrypted material starts out with a 64-bit random prefix, which - serves as an encrypted random CFB initialization vector, and - following that is 16 bits of "key check" material, which is a - duplicate of the last 2 bytes of the random prefix. Encrypted key - check bytes detect if correct IDEA key was used to decrypt ciphertext. -*/ -{ int count; - word16 iv[4]; +/* + * Use IDEA in cipher feedback (CFB) mode to encrypt or decrypt a file. + * The encrypted material starts out with a 64-bit random prefix, which + * serves as an encrypted random CFB initialization vector, and + * following that is 16 bits of "key check" material, which is a + * duplicate of the last 2 bytes of the random prefix. Encrypted key + * check bytes detect if correct IDEA key was used to decrypt ciphertext. + */ +{ + int count, status = 0; extern byte textbuf[DISKBUFSIZE]; + struct IdeaCfbContext cfb; #define RAND_PREFIX_LENGTH 8 /* init CFB key */ - fill0(iv,sizeof(iv)); /* define initialization vector IV as 0 */ - initcfb_idea(iv,ideakey,decryp); + ideaCfbInit(&cfb, ideakey); - if (!decryp) /* encrypt-- insert key check bytes */ - { /* There is a random prefix followed by 2 key check bytes */ - int i; + if (!decryp) { /* encrypt-- insert key check bytes */ + /* There is a random prefix followed by 2 key check bytes */ - for (i=0; i0) - { ideacfb(textbuf,count); - fwrite(textbuf,1,count,g); + /* read and write the whole file in CFB mode... */ + count = (lenfile < DISKBUFSIZE) ? (int)lenfile : DISKBUFSIZE; + while (count && status == 0) { + if ((count = fread(textbuf,1,count,f)) <= 0) { + status = -3; + break; } - /* if text block was short, exit loop */ - } while (count==DISKBUFSIZE); + lenfile -= count; + if (decryp) + ideaCfbDecrypt(&cfb, textbuf, textbuf, count); + else + ideaCfbEncrypt(&cfb, textbuf, textbuf, count); + if (fwrite(textbuf,1,count,g) != count) + status = -3; + count = (lenfile < DISKBUFSIZE) ? (int)lenfile : DISKBUFSIZE; + } - close_idea(); /* Clean up data structures */ - burn(iv); /* burn sensitive data on stack */ + ideaCfbDestroy(&cfb); /* Clean up data structures */ burn(textbuf); /* burn sensitive data on stack */ - return(0); /* should always take normal return */ + return status; /* should always take normal return */ } /* idea_file */ @@ -523,15 +528,18 @@ int idea_file(byte *ideakey, boolean dec */ word16 mpi_checksum; -int read_mpi(unitptr r, FILE *f, boolean adjust_precision, boolean scrambled) -/* Read a mutiprecision integer from a file. - adjust_precision is TRUE iff we should call set_precision to the - size of the number read in. - scrambled is TRUE iff field is encrypted (protects secret key fields). - Returns the bitcount of the number read in, or returns a negative - number if an error is detected. -*/ -{ byte buf[MAX_BYTE_PRECISION+2]; +int read_mpi(unitptr r, FILE *f, boolean adjust_precision, + struct IdeaCfbContext *cfb) +/* + * Read a mutiprecision integer from a file. + * adjust_precision is TRUE iff we should call set_precision to the + * size of the number read in. + * scrambled is TRUE iff field is encrypted (protects secret key fields). + * Returns the bitcount of the number read in, or returns a negative + * number if an error is detected. + */ +{ + byte buf[MAX_BYTE_PRECISION+2]; unsigned int count; word16 bytecount,bitcount; @@ -542,16 +550,18 @@ int read_mpi(unitptr r, FILE *f, boolean bitcount = fetch_word16(buf); if (bits2units(bitcount) > global_precision) - return(-1); /* error -- possible corrupted bitcount */ + return -1; /* error -- possible corrupted bitcount */ bytecount = bits2bytes(bitcount); count = fread(buf+2,1,bytecount,f); if (count < bytecount) - return(-1); /* error -- premature eof */ + return -1; /* error -- premature eof */ - if (scrambled) /* decrypt the field */ - ideacfb(buf+2,bytecount); + if (cfb) { /* decrypt the field */ + ideaCfbSync(cfb); + ideaCfbDecrypt(cfb, buf+2, buf+2, bytecount); + } /* Update running checksum, in case anyone cares... */ mpi_checksum += checksum (buf, bytecount+2); @@ -562,30 +572,37 @@ int read_mpi(unitptr r, FILE *f, boolean countbits, then call set_precision, then recall mpi2reg again. */ - if (adjust_precision && bytecount) - { /* set the precision to that specified by the number read. */ + if (adjust_precision && bytecount) { + /* set the precision to that specified by the number read. */ + if (bitcount > MAX_BIT_PRECISION-SLOP_BITS) + return -1; set_precision(bits2units(bitcount+SLOP_BITS)); /* Now that precision is optimally set, call mpi2reg */ } - mpi2reg(r,buf); /* convert to internal format */ + if (mpi2reg(r,buf) == -1) /* convert to internal format */ + return -1; burn(buf); /* burn sensitive data on stack */ return (bitcount); } /* read_mpi */ -void write_mpi(unitptr n, FILE *f, boolean scrambled) -/* Write a multiprecision integer to a file. - scrambled is TRUE iff we should scramble field on the way out, - which is used to protect secret key fields. -*/ -{ byte buf[MAX_BYTE_PRECISION+2]; +void write_mpi(unitptr n, FILE *f, struct IdeaCfbContext *cfb) +/* + * Write a multiprecision integer to a file. + * scrambled is TRUE iff we should scramble field on the way out, + * which is used to protect secret key fields. + */ +{ + byte buf[MAX_BYTE_PRECISION+2]; short bytecount; bytecount = reg2mpi(buf,n); mpi_checksum += checksum (buf, bytecount+2); - if (scrambled) /* encrypt the field, skipping over the bitcount */ - ideacfb(buf+2,bytecount); + if (cfb) { /* encrypt the field, skipping over the bitcount */ + ideaCfbSync(cfb); + ideaCfbEncrypt(cfb, buf+2, buf+2, bytecount); + } fwrite(buf,1,bytecount+2,f); burn(buf); /* burn sensitive data on stack */ } /* write_mpi */ @@ -596,15 +613,16 @@ void write_mpi(unitptr n, FILE *f, boole int get_header_info_from_file(char *infile, byte *header, int count) /* Reads the first count bytes from infile into header. */ -{ FILE *f; +{ + FILE *f; fill0(header,count); /* open file f for read, in binary (not text) mode...*/ - if ((f = fopen(infile,"rb")) == NULL) - return(-1); + if ((f = fopen(infile,FOPRBIN)) == NULL) + return -1; /* read Cipher Type Byte, and maybe more */ count = fread(header,1,count,f); fclose(f); - return(count); /* normal return */ + return count; /* normal return */ } /* get_header_info_from_file */ @@ -612,17 +630,16 @@ int get_header_info_from_file(char *infi #define REASONABLE_DATE ((unsigned long) 0x27804180L) /* 91 Jan 01 00:00:00 */ -int make_signature_certificate(byte *certificate, MD5_CTX *MD, byte class, - unitptr n, unitptr d, unitptr p, unitptr q, unitptr u) +static +int make_signature_certificate(byte *certificate, struct MD5Context *MD, + byte class, unitptr e, unitptr d, unitptr p, unitptr q, unitptr u, unitptr n) /* Constructs a signed message digest in a signature certificate. Returns total certificate length in bytes, or returns negative error status. */ { byte inbuf[MAX_BYTE_PRECISION], outbuf[MAX_BYTE_PRECISION]; - byte mdpacket[17]; - byte *mdbufptr; - int i,j,certificate_length,blocksize,bytecount; + int i, j, certificate_length, blocksize,bytecount; word16 ske_length; word32 tstamp; byte *timestamp = (byte *) &tstamp; byte keyID[KEYFRAGSIZE]; @@ -632,50 +649,53 @@ int make_signature_certificate(byte *cer /* Note that RSA key must be at least big enough to encipher a complete message digest packet in a single RSA block. */ - blocksize = countbytes(n)-1; /* size of a plaintext block */ - if (blocksize < 31) - { fprintf(pgpout,"\n\007Error: RSA key length must be at least 256 bits.\n"); - return(-1); - } - - get_timestamp(timestamp); /* Timestamp when signature was made */ - if (tstamp < REASONABLE_DATE) /* complain about bad time/date setting */ - { fprintf(pgpout,PSTR("\n\007Error: System clock/calendar is set wrong.\n")); - return(-1); + blocksize = countbytes(n)-1; /* size of a plaintext block */ + if (blocksize < 31) { + fprintf(pgpout,"\n\007Error: RSA key length must be at least 256 bits.\n"); + return -1; + } + + get_timestamp(timestamp); /* Timestamp when signature was made */ + if (tstamp < REASONABLE_DATE) { + /* complain about bad time/date setting */ + fprintf(pgpout,LANG("\n\007Error: System clock/calendar is set wrong.\n")); + return -1; + } + convert_byteorder(timestamp,4); /* convert to external form */ + + /* Finish off message digest calculation with this information */ + MD_addbuffer (MD, &class, 1, 0); + MD_addbuffer (MD, timestamp, 4, md5buf); +/* We wrote the digest to a static variable because we want to keep it around + for random number generation later. Also make a note of that fact. */ + already_have_md5 = 1; + + if (!quietmode) { + fprintf(pgpout,LANG("Just a moment...")); /* RSA will take a while. */ + fflush(pgpout); } - convert_byteorder(timestamp,4); /* convert to external form */ - - /* Finish off message digest calculation with this information */ - MD_addbuffer (MD, &class, 1, FALSE); - MD_addbuffer (MD, timestamp, 4, TRUE); - -#ifdef XLOWFIRST /* Assumes LSB-first order */ - mdbufptr = MD->digest; /* point at actual message digest */ - for (i=0; idigest; /* point at actual message digest */ - for (i=1; i0) - { for (j=0; j0) - { val = 0; /* Validation period */ - put_word16(val, certificate+certificate_length); - certificate_length+=2; /* advance past word */ - mdlen-=2; - } - /* hopefully, mdlen is now zero. */ + for (j=0; jdigest; - certificate[certificate_length++] = *mdbufptr++; - certificate[certificate_length++] = *mdbufptr++; + certificate[certificate_length++] = md5buf[0]; + certificate[certificate_length++] = md5buf[1];; /* Now append the RSA-signed message digest packet: */ for (i=0; i 93-02-25 + */ + if (getsecretkey(GPK_ASKPASS, NULL, NULL, timestamp, NULL, NULL, + userid, n, e, d, p, q, u) < 0) + { + fclose(f); + return -1; /* problem with secret key file. error return. */ } certificate_length = make_signature_certificate(certificate, &MD, - K0_SIGNATURE_BYTE, n, d, p, q, u); + K0_SIGNATURE_BYTE, e, d, p, q, u, n); if (certificate_length < 0) - return(-1); /* error return from make_signature_certificate() */ + return -1; /* error return from make_signature_certificate() */ } /* end of scope for some buffers */ /* open file g for write, in binary (not text) mode...*/ tempring = tempfile(TMP_TMPDIR); - if ((g = fopen(tempring,"wb")) == NULL) - { fprintf(pgpout,PSTR("\n\007Can't create output file to update key ring.\n")); + if ((g = fopen(tempring,FOPWBIN)) == NULL) { + fprintf(pgpout,LANG("\n\007Can't create output file to update key ring.\n")); fclose(f); - return(-1); + return -1; } /* Copy pre-key and key to file g */ @@ -1138,18 +1173,22 @@ above user ID (y/N)? ")); fwrite(certificate,1,certificate_length,g); /* Add "trusty" control packet */ - write_trust (g, KC_SIGTRUST_ULTIMATE | KC_CONTIG); + write_trust (g, KC_SIGTRUST_ULTIMATE|KC_CONTIG|KC_SIG_CHECKED); /* Copy the remainder from file f to file g */ copyfile (f, g, -1L); - fclose(g); fclose(f); + if (write_error(g)) { + fclose(g); + return -1; + } + fclose(g); savetempbak(tempring,keyfile); - fprintf(pgpout, PSTR("\nKey signature certificate added.\n")); - return(0); /* normal return */ + fprintf(pgpout, LANG("\nKey signature certificate added.\n")); + return 0; /* normal return */ } /* signkey */ @@ -1157,18 +1196,22 @@ above user ID (y/N)? ")); /*======================================================================*/ int check_signaturefile(char *infile, char *outfile, boolean strip_signature, - boolean explicit_outfile_flag) -{ /* Check signature in infile for validity. Strip off the signature - and write the remaining packet to outfile. If strip_signature, - also write the signature to outfile.sig. If the packet after - the signature is a LITERAL2, use the filename from there unless - explict_outfile_flag is set. - */ - byte ctb,ctb2; /* Cipher Type Bytes */ + char *preserved_name) +/* Check signature in infile for validity. Strip off the signature + * and write the remaining packet to outfile. If strip_signature, + * also write the signature to outfile.sig. + * the original filename is stored in preserved_name + */ +{ + byte ctb,ctb2=0; /* Cipher Type Bytes */ char keyfile[MAX_PATH]; /* for getpublickey */ char sigfile[MAX_PATH]; /* .sig file if strip_signature */ - long fp; /* unused, just to satisfy getpublickey */ - int pktlen; /* unused, just to satisfy getpublickey */ + char plainfile[MAX_PATH]; /* buffer for getstring() */ +#ifndef CANONICAL_TEXT + char *tempFileName; /* Name for temporary uncanonicalized file */ + FILE *tempFile; +#endif /* !CANONICAL_TEXT */ + long fp; FILE *f; FILE *g; long start_text; /* marks file position */ @@ -1184,12 +1227,16 @@ int check_signaturefile(char *infile, ch byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ word32 dummystamp; byte userid[256]; - MD5_CTX MD; + struct MD5Context MD; + byte digest[16]; boolean separate_signature; - extern boolean moreflag; + boolean fixedLiteral = FALSE; /* Whether it's a fixed literal2 packet */ + extern char **myArgv; + extern int myArgc; char lit_mode = MODE_BINARY; unsigned char litfile[MAX_PATH]; - word32 text_len; + word32 text_len = -1; + int status; byte *mdextras; byte mdlensave; byte version; @@ -1197,25 +1244,30 @@ int check_signaturefile(char *infile, ch byte class; byte algorithm; byte mdlow2[2]; + char org_sys[5]; /* Name of originating system */ +#ifdef VMS + char *fdl; + short fdl_len; +#endif + int outbufoffset; fill0( keyID, KEYFRAGSIZE ); set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ - buildfilename(keyfile,PUBLIC_KEYRING_FILENAME); /* use default pathname */ + strcpy(keyfile, globalPubringName); /* use default pathname */ if (verbose) - fprintf(pgpout,PSTR("\nSignature file: %s, output file: %s\n"), + fprintf(pgpout,"check_signaturefile: infile = '%s', outfile = '%s'\n", infile,outfile); + if (preserved_name) + *preserved_name = '\0'; + /* open file f for read, in binary (not text) mode...*/ -#ifdef VMS - if ((f = fopen(infile,"rb","ctx=stm")) == NULL) -#else - if ((f = fopen(infile,"rb")) == NULL) -#endif /* VMS */ - { fprintf(pgpout,PSTR("\n\007Can't open ciphertext file '%s'\n"),infile); - return(-1); + if ((f = fopen(infile,FOPRBIN)) == NULL) { + fprintf(pgpout,LANG("\n\007Can't open ciphertext file '%s'\n"),infile); + return -1; } /******************** Read header CTB and length field ******************/ @@ -1245,21 +1297,21 @@ int check_signaturefile(char *infile, ch mdextras = certificate; /* pointer to extra material for MD calculation */ class = *certificate++; - if (class != SM_SIGNATURE_BYTE && class != SB_SIGNATURE_BYTE) - { (void) version_error(class, SM_SIGNATURE_BYTE); + if (class != SM_SIGNATURE_BYTE && class != SB_SIGNATURE_BYTE) { + (void) version_error(class, SM_SIGNATURE_BYTE); goto err1; } mdlen--; - if (mdlen>0) /* if more MD material is included... */ - { for (i=0; i0) { /* if more MD material is included... */ + for (i=0; i0) /* if more MD material is included... */ - { certificate+=2; /* skip past unused validity period field */ + + if (mdlen>0) { /* if more MD material is included... */ + certificate+=2; /* skip past unused validity period field */ mdlen-=2; } @@ -1277,15 +1329,14 @@ int check_signaturefile(char *infile, ch mdlow2[0] = *certificate++; mdlow2[1] = *certificate++; - /* We used to set precision here based on certificate, but it is - better to use the value based on n, since that was used when we - signed the message. The certificate length could be less than n's - length. We will call getpublickey here just to set the precision. - */ - (void)getpublickey(FALSE, verbose, keyfile, &fp, &pktlen, - keyID, (byte *)&dummystamp, userid, n, e); + /* getpublickey() sets precision for mpi2reg, if key not found: use + maximum precision to avoid error return from mpi2reg() */ + if (getpublickey(0, keyfile, &fp, NULL, keyID, + (byte *)&dummystamp, userid, n, e) < 0) + set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ - mpi2reg((unitptr)inbuf,certificate); /* get signed message digest */ + if (mpi2reg((unitptr)inbuf,certificate) == -1) /* get signed message digest */ + goto err1; certificate += countbytes((unitptr)inbuf)+2; if ((certificate-certbuf) != cert_length+3) @@ -1295,73 +1346,115 @@ int check_signaturefile(char *infile, ch start_text = ftell(f); /* mark position of text for later */ - if (fread(outbuf,1,1,f) < 1) /* see if any plaintext is there */ - { /* Signature certificate has no plaintext following it. + if (fread(outbuf,1,1,f) < 1) { /* see if any plaintext is there */ + /* Signature certificate has no plaintext following it. Must be in another file. Go look. */ separate_signature = TRUE; + if (preserved_name) /* let caller know there is no output file */ + strcpy(preserved_name, "/dev/null"); fclose(f); - fprintf(pgpout,PSTR("\nFile '%s' has signature, but with no text."),infile); - if (!file_exists(outfile)) - force_extension(outfile, ""); - if (file_exists(outfile)) - { fprintf(pgpout,PSTR("\nText is assumed to be in file '%s'.\n"),outfile); - } - else - { fprintf(pgpout,PSTR("\nPlease enter filename of text that signature applies to: ")); - getstring(outfile,59,TRUE); /* echo keyboard */ - if ((int)strlen(outfile) == 0) - return(-1); + fprintf(pgpout,LANG("\nFile '%s' has signature, but with no text."),infile); + if (myArgc > 3 && file_exists(myArgv[3])) { + outfile = myArgv[3]; + fprintf(pgpout,LANG("\nText is assumed to be in file '%s'.\n"),outfile); + } else { + strcpy(plainfile, outfile); + outfile = plainfile; + drop_extension(outfile); + + if (file_exists(outfile)) { + fprintf(pgpout,LANG("\nText is assumed to be in file '%s'.\n"),outfile); + } else { + if (batchmode) + return -1; + fprintf(pgpout,LANG("\nPlease enter filename of material that signature applies to: ")); + getstring(outfile,59,TRUE); /* echo keyboard */ + if ((int)strlen(outfile) == 0) + return -1; + } } /* open file f for read, in binary (not text) mode...*/ -#ifdef VMS - if ((f = fopen(outfile,"rb","ctx=stm")) == NULL) -#else /* VMS */ - if ((f = fopen(outfile,"rb")) == NULL) -#endif /* VMS */ - { fprintf(pgpout,PSTR("\n\007Can't open file '%s'\n"),outfile); - return(-1); + if ((f = fopen(outfile,FOPRBIN)) == NULL) { + fprintf(pgpout,LANG("\n\007Can't open file '%s'\n"),outfile); + return -1; } start_text = ftell(f); /* mark position of text for later */ - text_len = fsize(f); /* remember length of text */ - } /* had to open new input file */ - else - { separate_signature = FALSE; + text_len = fsize(f); /* remember length of text */ + } else { + separate_signature = FALSE; /* We just read 1 byte, so outbuf[0] should contain a ctb, maybe a CTB_LITERAL byte. */ ctb2 = outbuf[0]; - if (is_ctb(ctb2) && is_ctb_type(ctb2,CTB_LITERAL_TYPE)) + fixedLiteral = is_ctb_type(ctb2,CTB_LITERAL2_TYPE); + if (is_ctb(ctb2) && (is_ctb_type(ctb2,CTB_LITERAL_TYPE)||fixedLiteral)) { /* Read literal data */ text_len = getpastlength(ctb2, f); /* read packet length */ lit_mode = '\0'; fread (&lit_mode,1,1,f); /* get literal packet mode byte */ - if (lit_mode != MODE_TEXT && lit_mode != MODE_BINARY) - { fprintf(pgpout,"\n\007Error: Illegal mode byte %02x in literal packet.\n", + if (lit_mode != MODE_TEXT && lit_mode != MODE_BINARY && + lit_mode != MODE_LOCAL) + { + fprintf(pgpout,"\n\007Error: Illegal mode byte %02x in literal packet.\n", lit_mode); /* English-only diagnostic for debugging */ (void) version_error(lit_mode, MODE_BINARY); goto err1; } if (verbose) - fprintf(pgpout, PSTR("File type: '%c'\n"), lit_mode); + fprintf(pgpout, LANG("File type: '%c'\n"), lit_mode); /* Read literal file name, use it if possible */ litfile[0] = 0; fread (litfile,1,1,f); - if ((int)litfile[0] > MAX_PATH) - litfile[0] = 0; /* If too long for us, ignore it */ + if( fixedLiteral ) + /* Get corrected text_len value by subtracting the length of + the filename and the timestamp and mode byte and litfile length byte */ + text_len -= litfile[0] + sizeof(dummystamp) + 2; if (litfile[0] > 0) - fread (litfile+1,1,litfile[0],f); + { + if ((int)litfile[0] >= MAX_PATH) { + fseek(f, litfile[0], SEEK_CUR); + litfile[0] = 0; + } else { + fread (litfile+1,1,litfile[0],f); + } + } /* Use litfile if it's writeable and he didn't say an outfile */ - if (litfile[0] > 0) - { PascalToC( (char *)litfile ); + if (litfile[0]) { + PascalToC( (char *)litfile ); if (verbose) - fprintf(pgpout, PSTR("Original plaintext file name was: '%s'\n"), litfile); - if (!strcmp((char *) litfile, CONSOLE_FILENAME) || !explicit_outfile_flag) - { file_from_canon( (char *)litfile ); - if (file_ok_write( (char *)litfile )) - strcpy ( outfile, (char *)litfile ); - } + fprintf(pgpout, LANG("Original plaintext file name was: '%s'\n"), litfile); + if (preserved_name) + strcpy(preserved_name, (char *) litfile); + } + if (lit_mode == MODE_LOCAL) { + fread(org_sys, 1, 4, f); org_sys[4] = '\0'; +#ifdef VMS +#define LOCAL_TEST !strncmp("VMS ",org_sys,4) +#else +#define LOCAL_TEST FALSE +#endif + if (LOCAL_TEST) { +#ifdef VMS + fread(&fdl_len, 2, 1, f); + fdl = (char *) malloc(fdl_len); + fread(fdl, 1, fdl_len, f); + if ((g = fdl_create( fdl, fdl_len, outfile, (char *) litfile)) == NULL) + { + fprintf(pgpout,"\n\007Unable to create file %s\n", outfile); + return -1; + } + free(fdl); + if (preserved_name) + strcpy(preserved_name, (char *) litfile); + text_len -= (fdl_len + sizeof(fdl_len)); +#endif /* VMS */ + } else { + fprintf(pgpout,"\n\007Unrecognised local binary type %s\n",org_sys); + return -1; + } + } else { + /* Discard file creation timestamp for now */ + fread (&dummystamp, 1, sizeof(dummystamp), f); } - /* Discard file creation timestamp for now */ - fread (&dummystamp, 1, sizeof(dummystamp), f); start_text = ftell(f); /* mark position of text for later */ } /* packet is CTB_LITERAL_TYPE */ } @@ -1369,52 +1462,53 @@ int check_signaturefile(char *infile, ch /* Use keyID prefix to look up key... */ /* Get and validate public key from a key file: */ - if (getpublickey(FALSE, verbose, keyfile, &fp, &pktlen, - keyID, (byte *)&dummystamp, userid, n, e) < 0) + if (getpublickey(0, keyfile, &fp, NULL, keyID, + (byte *)&dummystamp, userid, n, e) < 0) { /* Can't get public key. Complain and process file copy anyway. */ - fprintf(pgpout,PSTR("\n\007WARNING: Can't find the right public key-- can't check signature integrity.\n")); + fprintf(pgpout,LANG("\n\007WARNING: Can't find the right public key-- can't check signature integrity.\n")); goto outsig; } /* Can't find public key */ - if (warn_signatures(keyfile, fp, pktlen, (char *)userid, TRUE) < 0) - goto err1; - - /* Recover message digest via public key */ - mp_modexp((unitptr)outbuf,(unitptr)inbuf,e,n); + count = rsa_public_decrypt(outbuf, (unitptr)inbuf, e, n); - fputc('.',pgpout); /* Signal RSA completion. */ - - /* Unblock message digest, and convert to external byte order: */ - count = postunblock(outbuf, (unitptr)outbuf, n); - if (count < 0) - { fprintf(pgpout,PSTR("\n\007Error: RSA-decrypted block is corrupted.\n\ -This may be caused either by corrupted data or by using the wrong RSA key.\n")); - goto outsig; /* Output data anyway */ - } + if (!quietmode) + fputc('.',pgpout); /* Signal RSA completion. */ /* outbuf should contain message digest packet */ /*==================================================================*/ /* Look at nested stuff within RSA block... */ -#ifdef XLOWFIRST - /* Position of algorithm byte assumes LSB-first byteorder... */ - if (outbuf[count-1] != MD5_ALGORITHM_BYTE) -#else - /* Position of algorithm byte assumes MSB-first byteorder... */ - if (outbuf[0] != MD5_ALGORITHM_BYTE) -#endif - { fprintf(pgpout,PSTR("\007\nUnrecognized message digest algorithm.\n")); - fprintf(pgpout,PSTR("This may require a newer version of PGP.\n")); - fprintf(pgpout,PSTR("Can't check signature integrity.\n")); + if (count == -7 || (count > 0 && count != sizeof(digest))) + { + fputs(LANG("\007\nUnrecognized message digest algorithm.\n\ +This may require a newer version of PGP.\n\ +Can't check signature integrity.\n"), pgpout); + goto outsig; /* Output data anyway */ + } + if (count == -5) { /* RSAREF returned malformed */ + fputs(LANG("\a\nMalformed or obsolete signature. Can't check signature integrity.\n"), + pgpout); + goto outsig; + } + if (count == -3) { /* Key too big */ + fputs(LANG("\a\nSigning key is too large. RSA keys may be no longer than 1024 bits,\n\ +due to limitations imposed by software provided by RSADSI.\n\ +Can't check signature integrity.\n"), pgpout); + goto outsig; + } + if (count < 0) { /* Catch-all */ + fprintf(pgpout,LANG("\n\007Error: RSA-decrypted block is corrupted.\n\ +This may be caused either by corrupted data or by using the wrong RSA key.\n")); goto outsig; /* Output data anyway */ } -#ifdef XLOWFIRST - if (outbuf[0] != mdlow2[0] || outbuf[1] != mdlow2[1]) -#else - if (outbuf[1] != mdlow2[0] || outbuf[2] != mdlow2[1]) -#endif - { fprintf(pgpout,PSTR("\n\007Error: RSA-decrypted block is corrupted.\n\ + /* Distinguish PKCS-compatible from pre-3.3 which has an extra byte */ + outbufoffset = (count==sizeof(digest)) ? 0 : 1; + + if (outbuf[outbufoffset] != mdlow2[0] || + outbuf[outbufoffset+1] != mdlow2[1]) + { + fprintf(pgpout,LANG("\n\007Error: RSA-decrypted block is corrupted.\n\ This may be caused either by corrupted data or by using the wrong RSA key.\n")); goto outsig; /* Output data anyway */ } @@ -1424,104 +1518,145 @@ This may be caused either by corrupted d MDfile0_len(&MD,f,text_len);/* compute a message digest from rest of file */ - MD_addbuffer (&MD, mdextras, mdlensave, TRUE); /* Finish message digest */ + MD_addbuffer (&MD, mdextras, mdlensave, digest); /* Finish message digest */ convert_byteorder(timestamp,4); /* convert timestamp from external form */ PascalToC((char *)userid); /* for display */ /* now compare computed MD with claimed MD */ -#ifdef XLOWFIRST - if (!equal_buffers((byte *)(MD.digest), outbuf, 16)) -#else - if (!equal_buffers((byte *)(MD.digest), outbuf+1, 16)) -#endif - { fprintf(pgpout,PSTR("\007\nWARNING: Bad signature, doesn't match file contents!\007\n")); - fprintf(pgpout,PSTR("\nBad signature from user \"%s\".\n"),EXTERNAL((char *)userid)); - fprintf(pgpout,PSTR("Signature made %s\n"),ctdate((word32 *)timestamp)); -#ifndef CANONICAL_TEXT /* native text format is not canonical text */ - /* NOTE: IF the signature is bad, AND this machine does not - use MSDOS-style canonical text as its native text format, - AND this is a detached signature certificate, AND this - file appears to contain ASCII text, THEN maybe we should - convert the file to canonical text form and try checking - the signature again. - This is because a detached signature certificate probably - means the file is not currently in a canonical text packet, - but it may have been in canonical text form when the - signature was created. - */ - if (class == SM_SIGNATURE_BYTE && separate_signature) - { if (is_text_file(outfile)) - fprintf(pgpout,PSTR("\n\007PGP may have problems checking signatures against text files\n\ -if the text was created on a different system with a different\n\ -text file format.\n")); - /* Unfortunately, we don't give the user a way to convert it. */ - } -#endif /* not CANONICAL_TEXT */ - if (moreflag) - { /* more will scroll the message off the screen */ - fprintf(pgpout, PSTR("\nPress ENTER to continue...")); +/* Assume MSB external byte ordering */ + if (!equal_buffers(digest, outbuf+outbufoffset, 16)) { +#ifndef CANONICAL_TEXT + /* IF the signature is bad, AND this machine does not use + MSDOS-stype canonical text as its native text format, AND + this is a detached signature certificate, AND this file + appears to contain non-canonical ASCII text, THEN we + convert the file to canonical text form and check the + signature again. This is because a detached signature + certificate probably means the file is not currently in + a canonical text packet, but it was in canonical text form + when the signature was created, so by re-canonicalizing + it we can check the signature. */ + if (class == SM_SIGNATURE_BYTE && separate_signature && is_text_file(outfile)) + { /* Reposition file to where the plaintext begins and canonicalize it */ + rewind( f ); + tempFileName = tempfile( TMP_WIPE | TMP_TMPDIR ); + if (verbose) + fprintf(stderr, "signature checking failed, trying in canonical mode\n"); + if( ( tempFile = fopen( tempFileName, FOPWPBIN ) ) != NULL ) + { /* We've opened a temporary work file, copy the text to it + with canonicalization */ + copyfile_to_canon( f, tempFile, -1L ); + + /* Move back to the start of the file and recalculate the MD */ + rewind( tempFile ); + MDfile0_len( &MD, tempFile, -1L ); + MD_addbuffer( &MD, mdextras, mdlensave, digest ); + + /* Clean up behind us */ + fclose( tempFile ); + rmtemp( tempFileName ); + + /* Check if the signature is OK this time round */ +/* Assume MSB external byte ordering */ + if(equal_buffers(digest, outbuf+outbufoffset, 16)) + goto goodsig; + } + } +#endif /* !CANONICAL_TEXT */ + + fprintf(pgpout,LANG("\007\nWARNING: Bad signature, doesn't match file contents!\007\n")); + fprintf(pgpout,LANG("\nBad signature from user \"%s\".\n"), + LOCAL_CHARSET((char *)userid)); + fprintf(pgpout,LANG("Signature made %s\n"),ctdate((word32 *)timestamp)); + if (moreflag && !batchmode) { + /* more will scroll the message off the screen */ + fprintf(pgpout, LANG("\nPress ENTER to continue...")); fflush(pgpout); getyesno('n'); } - goto outsig; /* Output data anyway */ + goto warnsig; /* Output data anyway */ } - fprintf(pgpout,PSTR("\nGood signature from user \"%s\".\n"),EXTERNAL((char *)userid)); - fprintf(pgpout,PSTR("Signature made %s\n"),ctdate((word32 *)timestamp)); +goodsig: + signature_checked = TRUE; /* set flag for batch processing */ + fprintf(pgpout,LANG("\nGood signature from user \"%s\".\n"), + LOCAL_CHARSET((char *)userid)); + fprintf(pgpout,LANG("Signature made %s\n"),ctdate((word32 *)timestamp)); + +warnsig: + /* warn only, don't ask if user wants to use the key */ + warn_signatures(keyfile, fp, (char *)userid, TRUE); outsig: /* Reposition file to where that plaintext begins... */ fseek(f,start_text,SEEK_SET); /* reposition file from last ftell */ if (separate_signature) - fprintf(pgpout,PSTR("\nSignature and text are separate. No output file produced. ")); - else /* signature precedes plaintext in file... */ - { /* produce a plaintext output file from signature file */ - if (file_exists(outfile)) - { fprintf(pgpout,PSTR("\n\007Output file '%s' already exists. Overwrite (y/N)? "),outfile); - if (!getyesno('n')) /* user said don't do it. */ - goto err1; /* abort operation */ - } + { + if (!quietmode) + fprintf(pgpout,LANG("\nSignature and text are separate. No output file produced. ")); + } else { + /* signature precedes plaintext in file... */ + /* produce a plaintext output file from signature file */ /* open file g for write, in binary or text mode...*/ -#ifdef CANONICAL_TEXT - if ((g = fopen(outfile,"wb")) == NULL) -#else - if ((g = fopen(outfile,(lit_mode==MODE_BINARY)?"wb":"w")) == NULL) -#endif - { fprintf(pgpout,PSTR("\n\007Can't create plaintext file '%s'\n"),outfile); - goto err1; + if (lit_mode==MODE_LOCAL) { +#ifdef VMS + if (status = fdl_copyfile2bin( f, g, text_len)) { /* Copy ok? */ + if (status > 0) + fprintf(stderr,"\n...copying to literal file\n"); + else + perror("\nError copying from work file"); + fdl_close(g); + goto err1; + } + fdl_close(g); +#endif /*VMS */ + } else { + if (lit_mode == MODE_BINARY) + g = fopen(outfile, FOPWBIN); + else + g = fopen(outfile, FOPWTXT); + if (g == NULL) { + fprintf(pgpout,LANG("\n\007Can't create plaintext file '%s'\n"),outfile); + goto err1; + } + CONVERSION = (lit_mode==MODE_TEXT)?EXT_CONV:NO_CONV; + if (lit_mode == MODE_BINARY) + status = copyfile(f, g, text_len); + else + status = copyfile_from_canon(f, g, text_len); + CONVERSION = NO_CONV; + if (write_error(g) || status < 0) { + fclose(g); + goto err1; + } + fclose(g); } - CONVERSION = (lit_mode == MODE_TEXT) ? EXT_CONV : NO_CONV; -#ifdef CANONICAL_TEXT - copyfile(f,g,-1L); /* copy rest of file from file f to g */ -#else - if (lit_mode == MODE_BINARY) - copyfile( f, g, -1L ); - else - copyfile_from_canon( f, g, -1L ); -#endif - CONVERSION = NO_CONV; - fclose(g); - - if (strip_signature) - { /* Copy signature to a .sig file */ + if (strip_signature) { + /* Copy signature to a .sig file */ strcpy (sigfile, outfile); force_extension(sigfile,SIG_EXTENSION); - if (file_exists(sigfile)) - { fprintf(pgpout,PSTR("\n\007Signature file '%s' already exists. Overwrite (y/N)? "),sigfile); + if (!force_flag && file_exists(sigfile)) { + fprintf(pgpout,LANG("\n\007Signature file '%s' already exists. Overwrite (y/N)? "), + sigfile); if (!getyesno('n')) goto err1; } - if ((g = fopen(sigfile,"wb")) == NULL) - { fprintf(pgpout,PSTR("\n\007Can't create signature file '%s'\n"),sigfile); + if ((g = fopen(sigfile,FOPWBIN)) == NULL) { + fprintf(pgpout,LANG("\n\007Can't create signature file '%s'\n"),sigfile); goto err1; } fseek (f,0L,SEEK_SET); copyfile (f,g,(unsigned long)(cert_length+ctb_llength(ctb)+1)); + if (write_error(g)) { + fclose(g); + goto err1; + } fclose(g); - fprintf(pgpout,PSTR("\nWriting signature certificate to '%s'\n"),sigfile); + if (!quietmode) + fprintf(pgpout,LANG("\nWriting signature certificate to '%s'\n"),sigfile); } } @@ -1529,25 +1664,23 @@ outsig: burn(outbuf); /* burn sensitive data on stack */ fclose(f); if (separate_signature) - { *outfile = '\0'; - return(0); /* normal return, no nested info */ - } - if (is_ctb(ctb2) && (is_ctb_type(ctb2,CTB_LITERAL_TYPE))) + return 0; /* normal return, no nested info */ + if (is_ctb(ctb2) && (is_ctb_type(ctb2,CTB_LITERAL_TYPE) || fixedLiteral)) /* we already stripped away the CTB_LITERAL */ - return(0); /* normal return, no nested info */ + return 0; /* normal return, no nested info */ /* Otherwise, it's best to assume a nested CTB */ - return(1); /* nested information return */ + return 1; /* nested information return */ badcert: /* Bad packet. Complain. */ - fprintf(pgpout,PSTR("\n\007Error: Badly-formed or corrupted signature certificate.\n")); - fprintf(pgpout,PSTR("File \"%s\" does not have a properly-formed signature.\n"),infile); + fprintf(pgpout,LANG("\n\007Error: Badly-formed or corrupted signature certificate.\n")); + fprintf(pgpout,LANG("File \"%s\" does not have a properly-formed signature.\n"),infile); /* Now just drop through to error exit... */ err1: burn(inbuf); /* burn sensitive data on stack */ burn(outbuf); /* burn sensitive data on stack */ fclose(f); - return(-1); /* error return */ + return -1; /* error return */ } /* check_signaturefile */ @@ -1555,14 +1688,25 @@ err1: int check_key_sig(FILE *fkey, long fpkey, int keypktlen, char *keyuserid, FILE *fsig, long fpsig, char *keyfile, char *siguserid, byte *xtimestamp, byte *sigclass) -{ /* Check signature of key in file fkey at position fpkey, using signature - in file fsig and position fpsig. keyfile tells the file to use to - look for the public key in to check the sig. Return 0 if OK, -1 if - we can't check the signature, -2 if bad or other problem. - */ +/* + * Check signature of key in file fkey at position fpkey, using signature + * in file fsig and position fpsig. keyfile tells the file to use to + * look for the public key in to check the sig. Return 0 if OK, + * -1 generic error + * -2 Can't find key + * -3 Key too big + * -4 Key too small + * -5 Maybe malformed RSA + * -6 Unknown PK algorithm + * -7 Unknown conventional algorithm + * -8 Unknown version + * -9 Malformed RSA packet + * -10 Malformed packet + * -20 BAD SIGNATURE + */ +{ byte ctb; /* Cipher Type Bytes */ - long fp; /* unused, just to satisfy getpublickey */ - int pktlen; /* unused, just to satisfy getpublickey */ + long fp; word16 cert_length; int i, count; byte certbuf[MAX_SIGCERT_LENGTH]; @@ -1571,8 +1715,8 @@ int check_key_sig(FILE *fkey, long fpkey byte inbuf[MAX_BYTE_PRECISION]; byte outbuf[MAX_BYTE_PRECISION]; byte keyID[KEYFRAGSIZE]; - MD5_CTX MD; - byte mdlensave; + struct MD5Context MD; + byte digest[16]; byte *mdextras; word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ @@ -1594,59 +1738,50 @@ int check_key_sig(FILE *fkey, long fpkey *certificate++ = ctb; /* copy ctb into certificate */ if (!is_ctb(ctb) || !is_ctb_type(ctb,CTB_SKE_TYPE)) - goto badcert2; /* complain and return bad status */ + goto badcert; /* complain and return bad status */ cert_length = getpastlength(ctb, fsig); /* read certificate length */ certificate += ctb_llength(ctb); /* either 1, 2, 4, or 8 */ if (cert_length > MAX_SIGCERT_LENGTH-3) /* Huge packet length */ - goto badcert2; /* complain and return bad status */ + goto badcert; /* complain and return bad status */ /* read whole certificate: */ if (fread((byteptr) certificate, 1, cert_length, fsig) < cert_length) /* bad packet length field */ - goto badcert2; /* complain and return bad status */ + goto badcert; /* complain and return bad status */ version = *certificate++; if (version_error(version, VERSION_BYTE)) - goto err2; + return -8; + + mdlen = *certificate++; /* length of material to be added to MD */ + if (version_error(mdlen, 5)) + return -8; - mdlensave = mdlen = *certificate++; /* length of material to be added to MD */ mdextras = certificate; /* pointer to extra material for MD calculation */ *sigclass = class = *certificate++; if (class != K0_SIGNATURE_BYTE && class != K1_SIGNATURE_BYTE && class != K2_SIGNATURE_BYTE && class != K3_SIGNATURE_BYTE && class != KC_SIGNATURE_BYTE) - { (void) version_error(class, K0_SIGNATURE_BYTE); - goto err2; - } - mdlen--; - - if (mdlen>0) /* if more MD material is included... */ - { for (i=0; i0) /* if more MD material is included... */ - { certificate+=2; /* skip past unused validity period word */ - mdlen-=2; + { + (void)version_error(class, K0_SIGNATURE_BYTE); + return -8; } - if (mdlen>0) /* if more MD material is included... */ - certificate+=mdlen; /* skip over the rest */ + for (i=0; ihash, sizeof(ideakey)); + memset(passwds->hash, 0, sizeof(passwds->hash)); + hpw = passwds; + passwds = passwds->next; + free(hpw); + } else { + if (!quietmode) + fprintf(pgpout,LANG("\nYou need a pass phrase to encrypt the file. ")); + if (batchmode || GetHashedPassPhrase((char *)ideakey,NOECHO2) <= 0) + { + fclose(f); + fclose(g); + return -1; + } } + /* + * Get an initial vector, and write out a new randseed.bin. + * We do this now so that we can use the random bytes from the + * user's keystroke timings. + */ + make_random_ideakey(ideakey, 16); - fprintf(pgpout,PSTR("Just a moment...")); /* this may take a while */ - fflush(pgpout); + if (!quietmode) { + fprintf(pgpout,LANG("Just a moment...")); /* this may take a while */ + fflush(pgpout); + } /* Now compress the plaintext and encrypt it with IDEA... */ squish_and_idea_file( ideakey, f, g, attempt_compression ); burn(ideakey); /* burn sensitive data on stack */ - burn(passphrase); - fclose(g); fclose(f); + if (write_error(g)) { + fclose(g); + return -1; + } + fclose(g); - return(0); + return 0; } /* idea_encryptfile */ /*======================================================================*/ +static byte (*keyID_list)[KEYFRAGSIZE] = NULL; + +static int getmyname(char *userid) { + char keyfile[MAX_PATH]; + unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; + word32 tstamp; byte *timestamp = (byte *) &tstamp; + long fp; + int pktlen; + + strcpy(keyfile, globalSecringName); + + getpublickey(GPK_SECRET, keyfile, &fp, + NULL, NULL, timestamp, userid, n, e); + + PascalToC((char *)userid); -int encryptfile(char *mcguffin, char *infile, char *outfile, + return(0); +} + +int encryptfile(char **mcguffins, char *infile, char *outfile, boolean attempt_compression) { - byte randompad[MAX_BYTE_PRECISION]; /* buffer of random pad bytes */ - int i,blocksize,ckp_length; + int i,ckp_length; FILE *f; FILE *g; - unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; - byte inbuf[MAX_BYTE_PRECISION]; - byte outbuf[MAX_BYTE_PRECISION]; - word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ - byte userid[256]; - byte ideakey[16]; /* must be big enough for make_random_ideakey */ - char keyfile[MAX_PATH]; /* for getpublickey */ - long fp; /* unused, just to satisfy getpublickey */ - int pktlen; /* unused, just to satisfy getpublickey */ + byte keybuf[MAX_BYTE_PRECISION]; /* This keeps our IDEA to encrypt */ + byte ideakey[24]; /* must be big enough for make_random_ideakey */ word32 chksum; - byte ver, alg; - + char keyfile[MAX_PATH]; + int keys_used = 0; - buildfilename(keyfile,PUBLIC_KEYRING_FILENAME); /* use default pathname */ + if (mcguffins == NULL || *mcguffins == NULL || **mcguffins == '\0') { + /* Well, we haven't gotten a user, lets die here */ + return -1; + } if (verbose) - fprintf(pgpout,PSTR("\nPlaintext file: %s, ciphertext file: %s\n"), + fprintf(pgpout,"encryptfile: infile = %s, outfile = %s\n", infile,outfile); - userid[0] = '\0'; - if (mcguffin) - strcpy((char *)userid,mcguffin);/* Who we are looking for (C string) */ - - /* Get and validate public key from a key file: */ - if (getpublickey(FALSE, TRUE, keyfile, &fp, &pktlen, NULL, timestamp, userid, n, e) < 0) - { return(-1); - } - - if (warn_signatures(keyfile, fp, pktlen, (char *)userid, FALSE) < 0) - return(-1); - - /* set_precision has been properly called by getpublickey */ - - /* Note that RSA key must be at least big enough to encipher a - complete conventional key packet in a single RSA block. - */ - - blocksize = countbytes(n)-1; /* size of a plaintext block */ - if (blocksize < 31) - { fprintf(pgpout,"\n\007Error: RSA key length must be at least 256 bits.\n"); - return(-1); - } - /* open file f for read, in binary (not text) mode...*/ -#ifdef VMS - if ((f = fopen( infile, "rb", "ctx=stm" )) == NULL) -#else - if ((f = fopen( infile, "rb" )) == NULL) -#endif + if ((f = fopen( infile, FOPRBIN )) == NULL) { - fprintf(pgpout,PSTR("\n\007Can't open plaintext file '%s'\n"), infile ); - return(-1); + fprintf(pgpout,LANG("\n\007Can't open plaintext file '%s'\n"), infile ); + return -1; } /* open file g for write, in binary (not text) mode...*/ - if ((g = fopen( outfile, "wb" )) == NULL) + if ((g = fopen( outfile, FOPWBIN )) == NULL) { - fprintf(pgpout,PSTR("\n\007Can't create ciphertext file '%s'\n"), outfile ); + fprintf(pgpout,LANG("\n\007Can't create ciphertext file '%s'\n"), outfile ); fclose(f); - return(-1); + return -1; } - /* Now we have to time some user keystrokes to get some random - bytes for generating a random IDEA cipher key. - We would have to solicit fewer keystrokes for random IDEA - key generation if we had already accumulated some keystrokes - incidental to some other purpose, such as asking for a password - to decode an RSA secret key so that a signature could be applied - to the message before encrypting it. + /* Now we have to generate a random session key and IV. + As part of this computation, we use the MD5 hash of the + current file, if it has previously been obtained due to a + signing operation. If it has not been obtained, we hash + the first 2K (for efficiency reasons) for input into + the key generatrion process. This is to ensure that + capturing a randseed.bin file will not allow reconstruction + of subsequent session keys without knowing the message + that was encrypted. (A session key only protects a + single message, so it is reasonable to assume that an + opponent trying to obtain a session key is trying to + obtain, and thus is ignorant of, the message it encrypts.) + + This is not perfect, but it's an improvement on how session + keys used to be generated, and can be changed in future + without compatibility worries. */ - ckp_length = make_random_ideakey(ideakey); - /* Returns an 16 byte random IDEA key */ + if (!already_have_md5) { + /* Obtain some random bits from the input file */ + struct MD5Context MD; -#ifdef XLOWFIRST /* Assumes LSB-first order */ - for (i=0; i 13 Dec 1992 + */ - /* convert RSA ciphertext block via reg2mpi and write to file */ + for (i = 0; mcguffins[i] != NULL; ++i) + ; + if (encrypt_to_self) + ++i; + keyID_list = xmalloc(i * KEYFRAGSIZE); + /* Iterate through users */ + for (; *mcguffins && **mcguffins ; ++mcguffins) { + strcpy(keyfile, globalPubringName); + /* use default pathname */ + + keys_used = + encryptkeyintofile(g, *mcguffins, keybuf, + keyfile, ckp_length, keys_used); + } /* for */ - write_mpi( (unitptr)outbuf, g, FALSE ); + if (!keys_used) { + fclose(f); + fclose(g); + free(keyID_list); + return -1; + } - burn(inbuf); /* burn sensitive data on stack */ - burn(outbuf); /* burn sensitive data on stack */ + /* encrypt to myself if need be */ + if (encrypt_to_self) { + if (!*my_name) + /* Find our name from our keyring */ + getmyname(my_name); + if (*my_name) + /* If we were successful */ + keys_used = + encryptkeyintofile(g, my_name, keybuf, + keyfile, ckp_length, keys_used); + } + free(keyID_list); - /** Finished with RSA block containing IDEA key. */ + /* Finished with RSA block containing IDEA key. */ /* Now compress the plaintext and encrypt it with IDEA... */ squish_and_idea_file( ideakey, f, g, attempt_compression ); + burn(keybuf); /* burn the Idea Key Packet */ burn(ideakey); /* burn sensitive data on stack */ - fclose(g); fclose(f); + if (write_error(g)) { + fclose(g); + return -1; + } + fclose(g); - return(0); + return 0; } /* encryptfile */ +static int +encryptkeyintofile(FILE *g, char *mcguffin, byte *keybuf, + char *keyfile, int ckp_length, int keys_used) { + int i; + unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; + byte keyID[KEYFRAGSIZE]; + byte inbuf[MAX_BYTE_PRECISION]; + byte outbuf[MAX_BYTE_PRECISION]; + word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ + byte userid[256]; + long fp; + int blocksize; + byte ver, alg; + byte (*keyp)[KEYFRAGSIZE]; + + + /* This "loop" is so we can break out at opportune moments */ + do { + userid[0] = '\0'; + + strcpy((char *)userid,mcguffin); + /* Who we are looking for (C string) */ + + /* Get and validate public key from a key file: + * We will be nice and ask the user ONCE (and ONLY once) + * for a keyfile if its not in the default. + */ + + if (getpublickey((quietmode?0:GPK_SHOW)|GPK_NORVK, keyfile, &fp, NULL, NULL, + timestamp, userid, n, e) < 0) + { + fprintf(pgpout, LANG("\n\007Cannot find the public key matching userid '%s'\n\ +This user will not be able to decrypt this message.\n"), + LOCAL_CHARSET(mcguffin)); + continue; + } + + /* Make sure we haven't already used this key */ + extract_keyID(keyID, n); + for (keyp = keyID_list; keyp < keyID_list+keys_used; ++keyp) { + if (!memcmp(keyp, keyID, KEYFRAGSIZE)) + break; + } + + if (keyp < keyID_list + keys_used) { + /* This key was already specified. Quietly ignore it. */ + continue; + } + + /* Add this keyID to the list of keys used so far */ + memcpy(keyp, keyID, KEYFRAGSIZE); + + PascalToC((char *)userid); + if (warn_signatures(keyfile, fp, (char *)userid, + FALSE) < 0) { + fprintf(pgpout, "Ok, skipping userid %s\n", mcguffin); + continue; + } + + /* set_precision has been properly called by getpublickey */ + + /* Note that RSA key must be at least big enough + to encipher a complete conventional key packet + in a single RSA block. + */ + + blocksize = countbytes(n)-1; + /* size of a plaintext block */ + + if (blocksize < 31) { + fprintf(pgpout,"\n\007Error: RSA key length must be at least 256 bits.\n"); + fprintf(pgpout, "Skipping userid %s\n", mcguffin); + continue; + } + +#ifdef MR_DEBUG + /* XXX This is dangerous... This will print out the + * IDEA Key, which is a breach of security! + */ + fprintf(pgpout, "Idea Key: "); + for (i = 0; i < ckp_length; i++) + fprintf(pgpout, "%02X ", keybuf[i]); + fprintf(pgpout, "\n"); +#endif + i = rsa_public_encrypt((unitptr)outbuf, keybuf, ckp_length, e, n); + if (i < 0) { + if (i == -4) { + fprintf(pgpout,"\n\007Error: RSA key length must be at least 256 bits.\n"); + } else if (i == -3) { + fputs(LANG("\a\nError: key is too large. RSA keys may be no longer than 1024 bits,\n\ +due to limitations imposed by software provided by RSADSI.\n"), pgpout); + } else { + fprintf(pgpout, "\a\nUnexpected error %d encrypting\n", i); + } + fprintf(pgpout, LANG("Skipping userid %s\n"), mcguffin); + continue; + } + + /* write out header record to outfile ... */ + + /* PKE is Public Key Encryption */ + write_ctb_len (g, CTB_PKE_TYPE, + 1+KEYFRAGSIZE+1+2+countbytes((unitptr)outbuf), + FALSE); + + /* Write version byte */ + ver = VERSION_BYTE; + fwrite (&ver, 1, 1, g); + + writekeyID( n, g ); + /* write msg prefix fragment of modulus n */ + + /* Write algorithm byte */ + alg = RSA_ALGORITHM_BYTE; + fwrite (&alg, 1, 1, g); + + /* convert RSA ciphertext block via reg2mpi and + * write to file + */ + + write_mpi( (unitptr)outbuf, g, FALSE ); + + burn(inbuf); /* burn sensitive data on stack */ + burn(outbuf); /* burn sensitive data on stack */ + ++keys_used; + + } while (0); + + return keys_used; +} /* encryptkeyintofile */ + /*======================================================================*/ int make_literal(char *infile, char *outfile, char lit_mode, char *literalfile) -{ /* Prepend a CTB_LITERAL prefix to a file. Convert to canonical form if - lit_mode is MODE_TEXT. - */ +/* + * Prepend a CTB_LITERAL prefix to a file. Convert to canonical form if + * lit_mode is MODE_TEXT. + */ +{ char lfile[MAX_PATH]; FILE *f; FILE *g; + int status = 0; +#ifdef VMS + char *fdl; + short fdl_len; +#endif /* VMS */ + word32 flen, fpos; word32 dummystamp = 0; if (verbose) - fprintf(pgpout,PSTR("\nInput plaintext file: %s, Output plaintext file: %s\n"), - infile,outfile); + fprintf(pgpout,"make_literal: infile = %s, outfile = %s, mode = '%c', literalfile = '%s'\n", + infile,outfile,lit_mode,literalfile); /* open file f for read, in binary or text mode...*/ -#ifdef CANONICAL_TEXT - if ((f = fopen(infile,"rb")) == NULL) /* Always binary */ -#else - if ((f = fopen(infile,(lit_mode==MODE_BINARY)?"rb":"r")) == NULL) -#endif - { fprintf(pgpout,PSTR("\n\007Can't open input plaintext file '%s'\n"),infile); - return(-1); + +#ifdef VMS + if (lit_mode == MODE_LOCAL) { + if (!(fdl_generate(infile, &fdl, &fdl_len ) & 01)) { + fprintf(pgpout,LANG("\n\007Can't open input plaintext file '%s'\n"),infile); + return -1; + } + } +#endif /*VMS*/ + if (lit_mode == MODE_TEXT) + f = fopen(infile, FOPRTXT); + else + f = fopen(infile, FOPRBIN); + if (f == NULL) { + fprintf(pgpout,LANG("\n\007Can't open input plaintext file '%s'\n"),infile); + return -1; } flen = fsize(f); /* open file g for write, in binary (not text) mode... */ - if ((g = fopen( outfile,"wb" )) == NULL) - { fprintf(pgpout, PSTR("\n\007Can't create plaintext file '%s'\n"), outfile ); + if ((g = fopen( outfile,FOPWBIN )) == NULL) { + fprintf(pgpout, LANG("\n\007Can't create plaintext file '%s'\n"), outfile ); goto err1; } - if (lit_mode == MODE_BINARY) - write_ctb_len (g, CTB_LITERAL_TYPE, flen, FALSE); - else -#ifdef CANONICAL_TEXT - write_ctb_len (g, CTB_LITERAL_TYPE, flen, FALSE); -#else - /* Will put in size field later for text mode */ - write_ctb_len (g, CTB_LITERAL_TYPE, 0L, TRUE); -#endif - fwrite ( &lit_mode, 1, 1, g ); /* write lit_mode */ - /* write literalfile name */ - if (literalfile == NULL) - { /* Put in a zero byte to indicate no filename */ - fputc ('\0', g); - } - else - { strcpy( lfile, literalfile ); + if (literalfile == NULL) { + /* Put in a zero byte to indicate no filename */ + lfile[0] = '\0'; + } else { + strcpy( lfile, literalfile ); file_to_canon( lfile ); CToPascal( lfile ); - fwrite ( lfile, 1, lfile[0]+1, g); - } - /* Dummy file creation timestamp */ - fwrite ( &dummystamp, 1, sizeof(dummystamp), g); + } +#ifdef USE_LITERAL2 +#define LENGTH_FIELD (flen + (unsigned char) lfile[0] + 6) +#define LIT_TYPE CTB_LITERAL2_TYPE +#else +#define LENGTH_FIELD flen +#define LIT_TYPE CTB_LITERAL_TYPE +#endif + if (lit_mode == MODE_BINARY) + write_ctb_len (g, LIT_TYPE, LENGTH_FIELD, FALSE); +#ifdef VMS + else if (lit_mode == MODE_LOCAL) + write_ctb_len (g, CTB_LITERAL2_TYPE, flen + fdl_len + sizeof(fdl_len) + 6, TRUE); +#endif /* VMS */ + else /* Will put in size field later for text mode */ + write_ctb_len (g, LIT_TYPE, 0L, TRUE); +#ifdef USE_LITERAL2 fpos = ftell(g); +#endif + fwrite ( &lit_mode, 1, 1, g ); /* write lit_mode */ - CONVERSION = (lit_mode == MODE_TEXT) ? INT_CONV : NO_CONV; -#ifdef CANONICAL_TEXT - copyfile( f, g, -1L ); /* Simple copy on canonical systems */ - CONVERSION = NO_CONV; + if (lit_mode == MODE_LOCAL) { +#ifdef VMS + write_litlocal( g, fdl, fdl_len); + free(fdl); #else - if (lit_mode == MODE_BINARY) - copyfile( f, g, -1L ); - else - { copyfile_to_canon( f, g, -1L ); + ; /* Null statement if we don't have anything to do! */ +#endif /* VMS */ + } else { + /* write literalfile name */ + fwrite (lfile, 1, (unsigned char) lfile[0]+1, g); + /* Dummy file creation timestamp */ + fwrite ( &dummystamp, 1, sizeof(dummystamp), g); + } +#ifndef USE_LITERAL2 + fpos = ftell(g); +#endif + + if ((lit_mode == MODE_BINARY) || (lit_mode == MODE_LOCAL)) { + if (copyfile( f, g, -1L )) { + fprintf(pgpout,"\n\007Unable to append to literal plaintext file"); + perror("\n"); + fclose(g); + goto err1; + } + } else { + CONVERSION = (lit_mode == MODE_TEXT) ? INT_CONV : NO_CONV; + status = copyfile_to_canon( f, g, -1L ); CONVERSION = NO_CONV; /* Re-write CTB with correct length info */ rewind (g); - write_ctb_len (g, CTB_LITERAL_TYPE, fsize(f)-fpos, TRUE); + write_ctb_len (g, LIT_TYPE, fsize(g)-fpos, TRUE); + } + if (write_error(g) || status < 0) { + fclose(g); + goto err1; } -#endif - fclose(g); fclose(f); - return(0); /* normal return */ + return 0; /* normal return */ err1: fclose(f); - return(-1); /* error return */ + return -1; /* error return */ } /* make_literal */ +#undef LENGTH_FIELD +#undef LIT_TYPE /*======================================================================*/ -int strip_literal(char *infile, char *outfile, boolean explicit_outfile_flag, +int strip_literal(char *infile, char *outfile, char *preserved_name, char *lit_mode) -{ /* Strip off literal prefix from infile, copying to outfile. - Get lit_mode and literalfile info from - the prefix. Replace outfile with literalfile unless - literalfile is illegal or explicit_outfile_flag is TRUE. - If lit_mode is MODE_TEXT, convert from canonical form as we - copy the data. - */ +/* + * Strip off literal prefix from infile, copying to outfile. + * Get lit_mode and literalfile info from + * the prefix. Replace outfile with literalfile unless + * literalfile is illegal + * the original filename is stored in preserved_name + * If lit_mode is MODE_TEXT, convert from canonical form as we + * copy the data. + */ +{ byte ctb; /* Cipher Type Byte */ FILE *f; FILE *g; word32 LITlength = 0; unsigned char litfile[MAX_PATH]; word32 dummystamp; - + char org_sys[5]; /* Name of originating system */ + int status; +#ifdef VMS + char *fdl; + short fdl_len; +#endif *lit_mode = MODE_BINARY; if (verbose) - fprintf(pgpout,PSTR("\nInput plaintext file: %s, Output plaintext file: %s\n"), + fprintf(pgpout,"strip_literal: infile = %s, outfile = %s\n", infile,outfile); + if (preserved_name) + *preserved_name = '\0'; + /* open file f for read, in binary (not text) mode...*/ -#ifdef VMS - if ((f = fopen(infile,"rb","ctx=stm")) == NULL) -#else - if ((f = fopen(infile,"rb")) == NULL) -#endif - { fprintf(pgpout,PSTR("\n\007Can't open input plaintext file '%s'\n"),infile); - return(-1); + if ((f = fopen(infile,FOPRBIN)) == NULL) { + fprintf(pgpout,LANG("\n\007Can't open input plaintext file '%s'\n"),infile); + return -1; } fread(&ctb,1,1,f); /* read Cipher Type Byte */ - if (!is_ctb(ctb) || !(is_ctb_type(ctb,CTB_LITERAL_TYPE))) - { /* debug message in English only -- something got corrupted */ + if (!is_ctb(ctb) || !(is_ctb_type(ctb,CTB_LITERAL_TYPE) || + is_ctb_type(ctb,CTB_LITERAL2_TYPE))) + { + /* debug message in English only -- something got corrupted */ fprintf(pgpout,"\n\007'%s' is not a literal plaintext file.\n",infile); fclose(f); - return(-1); + return -1; } LITlength = getpastlength(ctb, f); /* read packet length */ @@ -2206,69 +2527,110 @@ int strip_literal(char *infile, char *ou /* Read literal data */ *lit_mode = '\0'; fread (lit_mode,1,1,f); - if ((*lit_mode != MODE_BINARY) && (*lit_mode != MODE_TEXT)) - { (void) version_error(*lit_mode, MODE_TEXT); + if ((*lit_mode != MODE_BINARY) && (*lit_mode != MODE_TEXT) + && (*lit_mode != MODE_LOCAL)) + { + (void) version_error(*lit_mode, MODE_TEXT); fclose(f); - return(-1); + return -1; } if (verbose) - fprintf(pgpout, PSTR("File type: '%c'\n"), lit_mode); + fprintf(pgpout, LANG("File type: '%c'\n"), *lit_mode); /* Read literal file name, use it if possible */ litfile[0] = 0; - fread (litfile,1,1,f); - if ((int)litfile[0] > MAX_PATH) - litfile[0] = 0; /* If too long for us, ignore it */ - if (litfile[0] > 0) - fread (litfile+1,1,litfile[0],f); + fread (litfile,1,1,f); + if (is_ctb_type(ctb, CTB_LITERAL2_TYPE)) { + /* subtract header length: namelength + lengthbyte + modebyte + stamp */ + LITlength -= litfile[0] + 2 + sizeof(dummystamp); + } /* Use litfile if it's writeable and he didn't say an outfile */ - if (litfile[0] > 0) - { PascalToC( (char *)litfile ); - if (verbose) - fprintf(pgpout, PSTR("Original plaintext file name was: '%s'\n"), litfile); - if (!strcmp((char *) litfile, CONSOLE_FILENAME) || !explicit_outfile_flag) - { file_from_canon( (char *)litfile ); - if (file_ok_write( (char *)litfile )) - strcpy ( outfile, (char *)litfile ); + if (litfile[0] > 0) { + if ((int)litfile[0] >= MAX_PATH) { + fseek(f, litfile[0], SEEK_CUR); + litfile[0] = 0; + } else { + fread (litfile+1,1,litfile[0],f); } } - /* Discard file creation timestamp for now */ - fread (&dummystamp, 1, sizeof(dummystamp), f); - - if (file_exists( outfile )) - { fprintf(pgpout, PSTR("\n\007Output file '%s' already exists. Overwrite (y/N)? "), outfile ); - if (! getyesno( 'n' )) - goto err1; /* user said don't do it - abort operation */ + if (litfile[0]) { + PascalToC( (char *)litfile ); + if (verbose) + fprintf(pgpout, LANG("Original plaintext file name was: '%s'\n"), litfile); + if (preserved_name) + strcpy(preserved_name, (char *) litfile); } - - /* open file g for write, in binary or text mode... */ -#ifdef CANONICAL_TEXT - if ((g = fopen( outfile, "wb" )) == NULL) /* Always binary */ + if (*lit_mode == MODE_LOCAL) { + fread(org_sys, 1, 4, f); org_sys[4] = '\0'; +#ifdef VMS +#define LOCAL_TEST !strncmp("VMS ",org_sys,4) #else - if ((g = fopen( outfile, (*lit_mode==MODE_BINARY)?"wb":"w" )) == NULL) +#define LOCAL_TEST FALSE #endif - { fprintf(pgpout, PSTR("\n\007Can't create plaintext file '%s'\n"), outfile ); - goto err1; + if (LOCAL_TEST) { +#ifdef VMS + remove(outfile); /* Prevent litter, we recreate the file with correct chars. */ + fread(&fdl_len, 2, 1, f); + fdl = (char *) malloc(fdl_len); + fread(fdl, 1, fdl_len, f); + if ((g = fdl_create( fdl, fdl_len, outfile, (char *) litfile)) == NULL) { + fprintf(pgpout,"\n\007Unable to create file %s\n", outfile); + return -1; + } + free(fdl); + if (preserved_name) + strcpy(preserved_name, (char *) litfile); + LITlength -= (fdl_len + sizeof(fdl_len)); +#endif /* VMS */ + } else { + fprintf(pgpout,"\n\007Unrecognised local binary type %s\n",org_sys); + return -1; + } + } else { + /* Discard file creation timestamp for now */ + fread (&dummystamp, 1, sizeof(dummystamp), f); } - /* copy rest of literal plaintext file */ - CONVERSION = (*lit_mode == MODE_TEXT) ? EXT_CONV : NO_CONV; -#ifdef CANONICAL_TEXT - copyfile( f, g, LITlength ); /* Simple copy on canonical systems */ -#else - if (*lit_mode == MODE_BINARY) - copyfile( f, g, LITlength ); - else - copyfile_from_canon( f, g, LITlength ); -#endif - CONVERSION = NO_CONV; + if (*lit_mode==MODE_LOCAL) { +#ifdef VMS + if (status = fdl_copyfile2bin( f, g, LITlength)) { /* Copy ok? */ + if (status > 0) + fprintf(stderr,"\n...copying to literal file\n"); + else + perror("\nError copying from work file"); + fdl_close(g); + goto err1; + } + fdl_close(g); +#endif /*VMS */ + } else { + if (*lit_mode == MODE_TEXT) + g = fopen(outfile, FOPWTXT); + else + g = fopen(outfile, FOPWBIN); + if (g == NULL) { + fprintf(pgpout, LANG("\n\007Can't create plaintext file '%s'\n"), outfile ); + goto err1; + } + /* copy rest of literal plaintext file */ + CONVERSION = (*lit_mode == MODE_TEXT) ? EXT_CONV : NO_CONV; + if (*lit_mode == MODE_BINARY) + status = copyfile(f, g, LITlength); + else + status = copyfile_from_canon(f, g, LITlength); + CONVERSION = NO_CONV; + if (write_error(g) || status < 0) { + fclose(g); + goto err1; + } + fclose(g); + } - fclose(g); fclose(f); - return(0); /* normal return */ + return 0; /* normal return */ err1: fclose(f); - return(-1); /* error return */ + return -1; /* error return */ } /* strip_literal */ @@ -2282,7 +2644,7 @@ int decryptfile(char *infile, char *outf byte ctbCKE; /* Cipher Type Byte */ FILE *f; FILE *g; - int count, status; + int count = 0, status, thiskey, gotkey, end_of_pkes; unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION], d[MAX_UNIT_PRECISION]; unit p[MAX_UNIT_PRECISION], q[MAX_UNIT_PRECISION], u[MAX_UNIT_PRECISION]; byte inbuf[MAX_BYTE_PRECISION]; @@ -2291,130 +2653,217 @@ int decryptfile(char *infile, char *outf word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ byte userid[256]; word32 flen; + word32 fpos = 0; byte ver, alg; + short realprecision = 0; word16 chksum; - - set_precision(MAX_UNIT_PRECISION); /* safest opening assumption */ + struct nkey { + byte keyID[KEYFRAGSIZE]; + struct nkey *next; + } *nkey, *nkeys = NULL; if (verbose) - fprintf(pgpout,PSTR("\nCiphertext file: %s, plaintext file: %s\n"), + fprintf(pgpout,"decryptfile: infile = %s, outfile = %s\n", infile,outfile); /* open file f for read, in binary (not text) mode...*/ -#ifdef VMS - if ((f = fopen(infile,"rb","ctx=stm")) == NULL) -#else - if ((f = fopen(infile,"rb")) == NULL) -#endif - { fprintf(pgpout,PSTR("\n\007Can't open ciphertext file '%s'\n"),infile); - return(-1); - } - - fread(&ctb,1,1,f); /* read Cipher Type Byte */ - if (!is_ctb(ctb)) - { fprintf(pgpout,PSTR("\n\007'%s' is not a cipher file.\n"),infile); - fclose(f); - return(-1); + if ((f = fopen(infile,FOPRBIN)) == NULL) { + fprintf(pgpout,LANG("\n\007Can't open ciphertext file '%s'\n"),infile); + return -1; } - /* PKE is Public Key Encryption */ - if (!is_ctb_type(ctb,CTB_PKE_TYPE)) - { fprintf(pgpout,PSTR("\n\007'%s' is not enciphered with a public key.\n"),infile); - fclose(f); - return(-1); - } + /* Now we have to keep reading in packets until we either get + * to a non PKE-type packet or we find our own... Once we find + * our own, we're gonna have to get our private key, and then + * keep going until we find the end of the PKE packets + * + * -derek 13 Dec 1992 + */ - getpastlength(ctb, f); /* read packet length */ + gotkey = end_of_pkes = 0; /* Set this flag now. */ + do { + thiskey = 0; + + set_precision(MAX_UNIT_PRECISION); + /* Need to set this EACH TIME... Sigh. This is because + * read_mpi needs to have a global_precision which is + * >= the size of the key. Therefore once we find the + * real key, we save off the precision and then we'll + * reset it later. -derek + */ + + fread(&ctb,1,1,f); /* read Cipher Type Byte */ + if (!is_ctb(ctb)) { + fprintf(pgpout,LANG("\n\007'%s' is not a cipher file.\n"),infile); + fclose(f); + return -1; + } - /* Read and check version */ - fread (&ver, 1, 1, f); - if (version_error(ver, VERSION_BYTE)) - { fclose (f); - return (-1); - } - - fread(keyID,1,KEYFRAGSIZE,f); /* read key ID */ - /* Use keyID prefix to look up key. */ - - /* Read and check algorithm */ - fread (&alg, 1, 1, f); - if (version_error(alg, RSA_ALGORITHM_BYTE)) - { fclose (f); - return (-1); - } - - /* Get and validate secret key from a key file: */ - if (getsecretkey(FALSE, TRUE, NULL, keyID, timestamp, NULL, NULL,userid, - n, e, d, p, q, u) < 0) - { fclose(f); - return(-1); - } + /* PKE is Public Key Encryption */ + if (!is_ctb_type(ctb,CTB_PKE_TYPE)) { + end_of_pkes = 1; + continue; + } - /* Note that RSA key must be at least big enough to encipher a - complete conventional key packet in a single RSA block. */ + getpastlength(ctb, f); /* read packet length */ - /*==================================================================*/ - /* read ciphertext block, converting to internal format: */ - read_mpi((unitptr)inbuf, f, FALSE, FALSE); + /* Read and check version */ + fread (&ver, 1, 1, f); + if (version_error(ver, VERSION_BYTE)) { + fclose (f); + return (-1); + } - fprintf(pgpout,PSTR("Just a moment...")); /* RSA will take a while. */ - fflush(pgpout); + fread(keyID,1,KEYFRAGSIZE,f); /* read key ID */ + /* Use keyID prefix to look up key. */ - rsa_decrypt((unitptr)outbuf, (unitptr)inbuf, d, p, q, u); + /* Add this keyID to the list of keys read in */ + nkey = (struct nkey *) malloc(sizeof(struct nkey)); + if (nkey == NULL) { + fprintf(stderr, LANG("\n\007Out of memory.\n")); + exitPGP(7); + } + memcpy(nkey->keyID, keyID, KEYFRAGSIZE); + nkey->next = nkeys; + nkeys = nkey; - fputc('.',pgpout); /* Signal RSA completion. */ + /* Read and check algorithm */ + fread (&alg, 1, 1, f); + if (version_error(alg, RSA_ALGORITHM_BYTE)) { + fclose (f); + return (-1); + } - if ((count = postunblock(outbuf, (unitptr)outbuf, n)) < 0) - { fprintf(pgpout,PSTR("\n\007Error: RSA-decrypted block is corrupted.\n\ + if (!gotkey) /* Only do this if we havent already */ + /* Get and validate secret key from a key file: */ + if (getsecretkey(GPK_GIVEUP|(quietmode?0:GPK_SHOW), + NULL, keyID, timestamp, NULL, NULL, + userid, n, e, d, p, q, u) == 0) + { + thiskey = gotkey = 1; + realprecision = global_precision; + } else { + set_precision(MAX_UNIT_PRECISION); + } + /* DAMN this... This is REALLY frustrating, that I have to + * do this... Basically, if I go to getsecretkey, it will + * set the precision, and the precision might NOT be correct + * if the key I get is not correct, so I have to set the + * precision NUMEROUS times in this loop.. This sucks, + * but its the only way. Sigh. + * + * -derek 13 Dec 1992 + */ + + /* Note that RSA key must be at least big enough + to encipher a complete conventional key packet in + a single RSA block. */ + + /*========================================================*/ + /* read ciphertext block, converting to internal format: */ + read_mpi((unitptr)inbuf, f, FALSE, FALSE); + + if (thiskey) { + if (!quietmode) { + fprintf(pgpout,LANG("Just a moment...")); + /* RSA will take a while. */ + fflush(pgpout); + } + count = rsa_private_decrypt(outbuf, (unitptr)inbuf, + e, d, p, q, u, n); + if (count < 0) { + if (count == -3) { + fputs(LANG("\a\nError: key is too large. RSA keys may be no longer than 1024 bits,\n\ +due to limitations imposed by software provided by RSADSI.\n"), pgpout); + } else if (count == -9 || count == -7) { + fprintf(pgpout, LANG("\n\007Error: RSA-decrypted block is corrupted.\n\ This may be caused either by corrupted data or by using the wrong RSA key.\n")); - fclose(f); - return(-1); + } else { + fprintf(pgpout, "\a\nUnexpected error %d decrypting\n", count); + } + fclose(f); + return count; + } + + if (!quietmode) + fputc('.',pgpout); + /* Signal RSA completion. */ + } + + fpos = ftell(f); /* Save this position */ + + } while (!end_of_pkes); /* Loop until end of PKE packets */ + + /* Should we list the recipients? */ + if (!gotkey || verbose) { + char *user; + + setkrent(NULL); + init_userhash(); + if (gotkey) /* verbose flag */ + fprintf(pgpout,"\nRecipients:\n"); + else + fprintf(pgpout,LANG("\nThis message can only be read by:\n")); + + for (nkey = nkeys; nkey; nkey = nkey->next) { + if ((user = user_from_keyID(nkey->keyID)) == NULL) + fprintf(pgpout, " keyID: %s\n", keyIDstring(nkey->keyID)); + else + fprintf(pgpout, " %s\n", LOCAL_CHARSET(user)); + } + endkrent(); + } + for (nkey = nkeys; nkey; ) { + nkey = nkey->next; + free(nkeys); + nkeys = nkey; } + /* Ok, Now lets clean up, and continue on to the rest of the file so + * that it can be decrypted properly. Things should be ok once I + * reset some stuff here... -derek + */ + if (gotkey) { + fseek(f, fpos, SEEK_SET); /* Get back to the Real McCoy! */ + set_precision(realprecision); /* reset the precision */ + } else { + /* No secret key, exit gracefully (NOT!) */ + fprintf(pgpout, LANG("\n\007You do not have the secret key needed to decrypt this file.\n")); + fclose(f); + return -1; + } /* Verify that top of buffer has correct algorithm byte */ --count; /* one less byte to drop algorithm byte */ -#ifdef XLOWFIRST /* Assumes LSB-first order */ - if (version_error(outbuf[count], IDEA_ALGORITHM_BYTE)) -#else /* Assumes MSB-first order */ - if (version_error(outbuf[0], IDEA_ALGORITHM_BYTE)) -#endif - { fclose(f); - return(-1); +/* Assume MSB external byte ordering */ + if (version_error(outbuf[0], IDEA_ALGORITHM_BYTE)) { + fclose(f); + return -1; } /* Verify checksum */ count -= 2; /* back up before checksum */ -#ifdef XLOWFIRST /* Assumes LSB-first order */ - chksum = fetch_word16(outbuf+count); - if (chksum != checksum(outbuf, count)) -#else /* Assumes MSB-first order */ +/* Assume MSB external byte ordering */ chksum = fetch_word16(outbuf+1+count); - if (chksum != checksum(outbuf+1, count)) -#endif - { fprintf(pgpout,PSTR("\n\007Error: RSA-decrypted block is corrupted.\n\ + if (chksum != checksum(outbuf+1, count)) { + fprintf(pgpout,LANG("\n\007Error: RSA-decrypted block is corrupted.\n\ This may be caused either by corrupted data or by using the wrong RSA key.\n")); fclose(f); - return(-1); + return -1; } /* outbuf should contain random IDEA key packet */ /*==================================================================*/ - if (file_exists( outfile )) - { fprintf(pgpout, PSTR("\n\007Output file '%s' already exists. Overwrite (y/N)? "), outfile ); - if (! getyesno( 'n' )) - goto err1; /* user said don't do it - abort operation */ - } - /* open file g for write, in binary (not text) mode... */ - if ((g = fopen( outfile, "wb" )) == NULL) - { fprintf(pgpout, PSTR("\n\007Can't create plaintext file '%s'\n"), outfile ); + + if ((g = fopen( outfile, FOPWBIN )) == NULL) { + fprintf(pgpout, LANG("\n\007Can't create plaintext file '%s'\n"), outfile ); goto err1; } fread(&ctbCKE,1,1,f); /* read Cipher Type Byte, should be CTB_CKE */ - if (!is_ctb(ctbCKE) || !is_ctb_type(ctbCKE,CTB_CKE_TYPE)) - { /* Should never get here. */ + if (!is_ctb(ctbCKE) || !is_ctb_type(ctbCKE,CTB_CKE_TYPE)) { + /* Should never get here. */ fprintf(pgpout,"\007\nBad or missing CTB_CKE byte.\n"); goto err1; /* Abandon ship! */ } @@ -2422,16 +2871,18 @@ This may be caused either by corrupted d flen = getpastlength(ctbCKE, f); /* read packet length */ /* Decrypt ciphertext file */ -#ifdef XLOWFIRST /* Assumes LSB-first order */ - status = idea_file( outbuf, DECRYPT_IT, f, g, flen ); -#else /* Assumes MSB-first order */ +/* Assume MSB external byte ordering */ status = idea_file( outbuf+1, DECRYPT_IT, f, g, flen ); -#endif - if (status < 0) - { fprintf(pgpout,PSTR("\n\007Error: Decrypted plaintext is corrupted.\n")); + if (status < 0) { + fprintf(pgpout,LANG("\n\007Error: Decrypted plaintext is corrupted.\n")); } - fputc('.',pgpout); /* show progress */ + if (!quietmode) + fputc('.',pgpout); /* show progress */ + if (write_error(g)) { + fclose(g); + goto err1; + } fclose(g); fclose(f); burn(inbuf); /* burn sensitive data on stack */ @@ -2441,8 +2892,8 @@ This may be caused either by corrupted d mp_burn(q); /* burn sensitive data on stack */ mp_burn(u); /* burn sensitive data on stack */ if (status < 0) /* if idea_file failed, then error return */ - return(status); - return(1); /* always indicate output file has nested stuff in it. */ + return status; + return 1; /* always indicate output file has nested stuff in it. */ err1: fclose(f); @@ -2452,7 +2903,7 @@ err1: mp_burn(p); /* burn sensitive data on stack */ mp_burn(q); /* burn sensitive data on stack */ mp_burn(u); /* burn sensitive data on stack */ - return(-1); /* error return */ + return -1; /* error return */ } /* decryptfile */ @@ -2464,78 +2915,107 @@ int idea_decryptfile(char *infile, char FILE *f; FILE *g; byte ideakey[16]; - byte passphrase[256]; - int status; + int status, retries = 0; + struct hashedpw *hpw, **hpwp; word32 flen; if (verbose) - fprintf(pgpout,PSTR("\nCiphertext file: %s, plaintext file: %s\n"), + fprintf(pgpout,"idea_decryptfile: infile = %s, outfile = %s\n", infile,outfile); /* open file f for read, in binary (not text) mode...*/ -#ifdef VMS - if ((f = fopen(infile,"rb","ctx=stm")) == NULL) -#else - if ((f = fopen(infile,"rb")) == NULL) -#endif - { fprintf(pgpout,PSTR("\n\007Can't open ciphertext file '%s'\n"),infile); - return(-1); + if ((f = fopen(infile,FOPRBIN)) == NULL) { + fprintf(pgpout,LANG("\n\007Can't open ciphertext file '%s'\n"),infile); + return -1; } - fread(&ctb,1,1,f); /* read Cipher Type Byte, should be CTB_CKE */ - - if (!is_ctb(ctb) || !is_ctb_type(ctb,CTB_CKE_TYPE)) - { /* Should never get here. */ - fprintf(pgpout,"\007\nBad or missing CTB_CKE byte.\n"); - goto err1; /* Abandon ship! */ + /* open file g for write, in binary (not text) mode... */ + if ((g = fopen( outfile, FOPWBIN )) == NULL) { + fprintf(pgpout, LANG("\n\007Can't create plaintext file '%s'\n"), outfile ); + goto err1; } - flen = getpastlength(ctb, f); /* read packet length */ + /* First, try all pre-specified passwords */ + hpwp = &passwds; + hpw = *hpwp; - if (file_exists( outfile )) - { fprintf(pgpout, PSTR("\n\007Output file '%s' already exists. Overwrite (y/N)? "), outfile ); - if (! getyesno( 'n' )) - goto err1; /* user said don't do it - abort operation */ - } + do /* while pass phrase is bad */ + { + fread(&ctb,1,1,f); /* read Cipher Type Byte, should be CTB_CKE */ - /* open file g for write, in binary (not text) mode... */ - if ((g = fopen( outfile, "wb" )) == NULL) - { fprintf(pgpout, PSTR("\n\007Can't create plaintext file '%s'\n"), outfile ); - goto err1; - } + if (!is_ctb(ctb) || !is_ctb_type(ctb,CTB_CKE_TYPE)) { + /* Should never get here. */ + fprintf(pgpout,"\007\nBad or missing CTB_CKE byte.\n"); + fclose(g); + goto err1; /* Abandon ship! */ + } + flen = getpastlength(ctb, f); /* read packet length */ - /* Get IDEA password, hashed */ - fprintf(pgpout,PSTR("\nYou need a pass phrase to decrypt this file. ")); - if (getideakey((char *)passphrase,(char *)ideakey,NOECHO1) <= 0) - { fclose(f); - fclose(g); - return(-1); - } + /* Get IDEA password, hashed */ + if (hpw) { + /* first try environment passwords */ + memcpy(ideakey, hpw->hash, sizeof(ideakey)); + } else { + fprintf(pgpout,LANG("\nYou need a pass phrase to decrypt this file. ")); + if (batchmode || GetHashedPassPhrase((char *)ideakey,NOECHO1) <= 0) + { + fclose(f); + fclose(g); + return -1; + } + } - fprintf(pgpout,PSTR("Just a moment...")); /* this may take a while */ - fflush(pgpout); + if (!quietmode) { + fprintf(pgpout,LANG("Just a moment...")); /* this may take a while */ + fflush(pgpout); + } - status = idea_file( ideakey, DECRYPT_IT, f, g, flen ); + status = idea_file( ideakey, DECRYPT_IT, f, g, flen ); + if (status == 0) { + if (hpw) { + /* "Use up" password. */ + *hpwp = hpw->next; + memset(hpw->hash, 0, sizeof(hpw->hash)); + free(hpw); + } + break; + } + if (hpw) { + /* Go to next available password */ + hpwp = &hpw->next; + hpw = *hpwp; + } else { + ++retries; + fprintf(pgpout,LANG("\n\007Error: Bad pass phrase.\n")); + } + + rewind(f); + rewind(g); + } while (status == -2 && retries < 2); burn(ideakey); /* burn sensitive data on stack */ - burn(passphrase); - fputc('.',pgpout); /* show progress */ + if (status == 0 && !quietmode) + fputc('.',pgpout); /* show progress */ + if (write_error(g)) { + fclose(g); + goto err1; + } fclose(g); fclose(f); - if (status < 0) /* if idea_file failed, then complain */ - { fprintf(pgpout,PSTR("\n\007Error: Bad pass phrase.\n")); + if (status < 0) { /* if idea_file failed, then complain */ remove(outfile); /* throw away our mistake */ - return(status); /* error return */ + return status; /* error return */ } - fprintf(pgpout,PSTR("Pass phrase appears good. ")); - return(1); /* always indicate output file has nested stuff in it. */ + if (!quietmode) + fprintf(pgpout,LANG("Pass phrase appears good. ")); + return 1; /* always indicate output file has nested stuff in it. */ err1: fclose(f); - return(-1); /* error return */ + return -1; /* error return */ } /* idea_decryptfile */ @@ -2547,22 +3027,18 @@ int decompress_file(char *infile, char * FILE *f; FILE *g; extern void lzhDecode( FILE *, FILE * ); - extern void unzip( FILE *, FILE * ); - if (verbose) fprintf(pgpout, PSTR("Decompressing plaintext...") ); + extern int unzip( FILE *, FILE * ); + if (verbose) fprintf(pgpout, LANG("Decompressing plaintext...") ); /* open file f for read, in binary (not text) mode...*/ -#ifdef VMS - if ((f = fopen(infile,"rb","ctx=stm")) == NULL) -#else - if ((f = fopen(infile,"rb")) == NULL) -#endif - { fprintf(pgpout,PSTR("\n\007Can't open compressed file '%s'\n"),infile); - return(-1); + if ((f = fopen(infile,FOPRBIN)) == NULL) { + fprintf(pgpout,LANG("\n\007Can't open compressed file '%s'\n"),infile); + return -1; } fread(&ctb,1,1,f); /* read and skip over Cipher Type Byte */ - if (!is_ctb_type( ctb, CTB_COMPRESSED_TYPE )) - { /* Shouldn't get here, or why were we called to begin with? */ + if (!is_ctb_type( ctb, CTB_COMPRESSED_TYPE )) { + /* Shouldn't get here, or why were we called to begin with? */ fprintf(pgpout,"\007\nBad or missing CTB_COMPRESSED byte.\n"); goto err1; /* Abandon ship! */ } @@ -2571,31 +3047,43 @@ int decompress_file(char *infile, char * /* The packet length is ignored. Assume it's huge. */ fread(&ctb,1,1,f); /* read and skip over compression algorithm byte */ - if (ctb != ZIP2_ALGORITHM_BYTE) - { /* We only know how to uncompress deflate-compressed data. We - may hit imploded or Lharc'ed data but treat it as an error just - the same */ - fprintf(pgpout,PSTR("\007\nUnrecognized compression algorithm.\n\ + if (ctb != ZIP2_ALGORITHM_BYTE) { + /* We only know how to uncompress deflate-compressed data. We + may hit imploded or Lharc'ed data but treat it as an error + just the same */ + fprintf(pgpout,LANG("\007\nUnrecognized compression algorithm.\n\ This may require a newer version of PGP.\n")); goto err1; /* Abandon ship! */ } /* open file g for write, in binary (not text) mode... */ - if ((g = fopen( outfile, "wb" )) == NULL) - { fprintf(pgpout, PSTR("\n\007Can't create decompressed file '%s'\n"), outfile ); + if ((g = fopen( outfile, FOPWBIN )) == NULL) { + fprintf(pgpout, LANG("\n\007Can't create decompressed file '%s'\n"), outfile ); goto err1; } - unzip( f, g ); - if (verbose) fprintf(pgpout, PSTR("done. ") ); - else fputc('.',pgpout); /* show progress */ + if (unzip( f, g )) { + fprintf(pgpout,LANG("\n\007Decompression error. Probable corrupted input.\n")); + goto err2; + } + + if (verbose) + fprintf(pgpout, LANG("done. ") ); + else if (!quietmode) + fputc('.',pgpout); /* show progress */ + + if (write_error(g)) + goto err2; fclose(g); fclose(f); - return(1); /* always indicate output file has nested stuff in it. */ + return 1; /* always indicate output file has nested stuff in it. */ + +err2: + fclose(g); err1: fclose(f); - return(-1); /* error return */ + return -1; /* error return */ -} /* decompress_file */ +} /* decompress_file */