--- pgp/src/pgp.c 2018/04/24 16:37:52 1.1 +++ pgp/src/pgp.c 2018/04/24 16:42:41 1.1.1.7 @@ -1,3833 +1,2163 @@ -/* - Pretty Good(tm) Privacy - RSA public key cryptography for the masses - Written by Philip Zimmermann, Phil's Pretty Good(tm) Software. - Beta test version 1.0 - Last revised 5 Jun 91 by PRZ - - PGP combines the convenience of the Rivest-Shamir-Adleman (RSA) - public key cryptosystem with the speed of fast conventional - cryptographic algorithms, fast message digest algorithms, data - compression, and sophisticated key management. And PGP performs - the RSA functions faster than most other software implementations. - PGP is RSA public key cryptography for the masses. - - Uses RSA Data Security, Inc. MD4 Message Digest Algorithm - for signatures. Uses the LZHUF algorithm for compression. - Uses my own algorithm, BassOmatic, for conventional encryption. - - (c) Copyright 1990 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 I 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. The source code for the MD4 - functions and the LZHUF functions were separately placed in the - public domain by their respective authors. See the PGP User's - Guide for more complete information about licensing, patent - restrictions on the RSA algorithm, trademarks, copyrights, and - export controls. Technical assistance from me is available for - an hourly fee. - - - PGP generally zeros its used stack and memory areas before exiting. - This avoids leaving sensitive information in RAM where other users - could find it later. The RSA library and keygen routines also - sanitize their own stack areas. This stack sanitizing has not been - checked out under all the error exit conditions, when routines exit - abnormally. Also, we must find a way to clear the C I/O library - file buffers, and the MSDOS disk buffers. - - The code in this source file (pgp.c) was hastily written, and it - shows. It has a lot of redundant code, developed by ad-hoc - "accretion" rather than by well-planned design. It isn't buggy, but - it needs to be reorganized to make it cleaner, clearer, and more - succinct. Maybe someday. Better and more typical examples of my - programming style can be seen in the RSA library code in rsalib.c - and keygen.c, and in the BassOmatic conventional encryption routines - in basslib.c and related files. - - If you modify this code, PLEASE preserve the style of indentation - used for {begin...end} blocks. It drives me bats to have to deal - with more than one style in the same program. - -*/ - - -#include /* for exit(), malloc(), free(), etc. */ -#include /* for printf(), tmpfile(), etc. */ -#include /* for timestamps and performance measurement */ -#include /* for strcat(), etc. */ -#include -#include /* for kbhit() */ - -#include "md4.h" /* for MD4 message digest stuff */ -#include "rsalib.h" -#include "rsaio.h" -#include "keygen.h" -#include "random.h" -#include "basslib.h" -#include "basslib2.h" - -#define KEYFRAGSIZE 8 /* # of bytes in key ID modulus fragment */ -#define SIZEOF_TIMESTAMP 4 /* 32-bit timestamp */ - -/* This macro is for burning sensitive data (byte arrays only) on stack */ -#define burn(x) fill0(x,sizeof(x)) - -/* -********************************************************************** -*/ - -/* Cipher Type Byte (CTB) definitions follow...*/ -#define CTB_DESIGNATOR 0x80 -#define is_ctb(c) (((c) & CTB_DESIGNATOR)==CTB_DESIGNATOR) -#define CTB_TYPE_MASK 0x7c -#define CTB_LLEN_MASK 0x03 - -/* length of length field of packet, in bytes (1, 2, 4, 8 bytes): */ -#define ctb_llength(ctb) ((int) 1 << (int) ((ctb) & CTB_LLEN_MASK)) - -#define is_ctb_type(ctb,type) (((ctb) & CTB_TYPE_MASK)==(4*type)) -#define CTB_BYTE(type,llen) (CTB_DESIGNATOR + (4*type) + llen) - -#define CTB_PKE_TYPE 1 /* packet encrypted with RSA public key */ -#define CTB_SKE_TYPE 2 /* packet signed with RSA secret key */ -#define CTB_MD_TYPE 3 /* message digest packet */ -#define CTB_CONKEY_TYPE 4 /* conventional key packet */ -#define CTB_CERT_SECKEY_TYPE 5 /* secret key certificate */ -#define CTB_CERT_PUBKEY_TYPE 6 /* public key certificate */ -#define CTB_COMPRESSED_TYPE 8 /* compressed data packet */ -#define CTB_CKE_TYPE 9 /* conventional-key-encrypted data */ -#define CTB_LITERAL_TYPE 12 /* raw data */ - -/* Unimplemented CTB packet types follow... */ -/* #define CTB_RAW1_TYPE 13 /* raw data, with filename, date, crc32 prefix */ -/* #define CTB_PATTERN_TYPE 14 /* unique file prefix autorecognition pattern */ -/* #define CTB_EXTENDED_TYPE 15 /* 2-byte CTB, 256 extra CTB types */ - -#define CTB_PKE CTB_BYTE(CTB_PKE_TYPE,1) - /* CTB_PKE len16 keyID mpi(RSA(CONKEYPKT)) */ - /* 1 2 SIZE countbytes()+2 */ -#define CTB_SKE CTB_BYTE(CTB_SKE_TYPE,1) - /* CTB_SKE len16 keyID mpi(RSA(MDPKT)) */ - /* 1 2 SIZE countbytes()+2 */ -#define CTB_MD CTB_BYTE(CTB_MD_TYPE,0) - /* CTB_MD len8 algorithm MD timestamp */ -#define CTB_CONKEY CTB_BYTE(CTB_CONKEY_TYPE,0) - /* CTB_CONKEY len8 algorithm key */ -#define CTB_CERT_SECKEY CTB_BYTE(CTB_CERT_SECKEY_TYPE,1) - /* CTB_CERT_SECKEY len16 timestamp userID mpi(n) mpi(e) mpi(d) mpi(p) mpi(q) mpi(u) crc16 */ -#define CTB_CERT_PUBKEY CTB_BYTE(CTB_CERT_PUBKEY_TYPE,1) - /* CTB_CERT_PUBKEY len16 timestamp userID mpi(n) mpi(e) crc16 */ - -/* Note that a "secret key compromised" certificate is exactly the same - as a public key certificate, but with mpi(e)==0. */ - -#define CTB_CKE CTB_BYTE(CTB_CKE_TYPE,3) - /* CTB_CKE ciphertext */ - -#define CTB_LITERAL CTB_BYTE(CTB_LITERAL_TYPE,3) - /* CTB_LITERAL data */ - -#define CTB_COMPRESSED CTB_BYTE(CTB_COMPRESSED_TYPE,3) - /* CTB_COMPRESSED compressedtext */ - -#define CTB_PATTERN CTB_BYTE(CTB_PATTERN_TYPE,0) - /* Unique 40-bit auto-recognition prefix pattern: B8 03 'P' 'R' 'Z' */ - -/* Conventional encryption algorithm selector bytes. */ -#define DES_ALGORITHM_BYTE 1 /* use the DES (unimplemented) */ -#define BASS_ALGORITHM_BYTE 2 /* use the BassOmatic */ - -/* Message digest algorithm selector bytes. */ -#define MD4_ALGORITHM_BYTE 1 /* MD4 message digest algorithm */ - -/* Data compression algorithm selector bytes. */ -#define LZH_ALGORITHM_BYTE 1 /* LZH compression algorithm */ - -#define is_secret_key(ctb) is_ctb_type(ctb,CTB_CERT_SECKEY_TYPE) - -#define MAX_SIGCERT_LENGTH (1+2 + KEYFRAGSIZE + 2+MAX_BYTE_PRECISION) - -#define MAX_KEYCERT_LENGTH (1+2+4+256 + 5*(2+MAX_BYTE_PRECISION)) - - -/* Global filenames and system-wide file extensions... */ -char CTX_EXTENSION[] = ".ctx"; -char PUB_EXTENSION[] = ".pub"; -char SEC_EXTENSION[] = ".sec"; -char SCRATCH_CTX_FILENAME[] = "_pgptemp.ctx"; -char SCRATCH_PTX_FILENAME[] = "_pgptemp.ptx"; -char SCRATCH_KEYRING_FILENAME[] = "_tmpring.pub"; /* gets modified */ -char PGPPATH[] = "PGPPATH"; /* environmental variable */ - -/* These files use the environmental variable PGPPATH as a default path: */ -char PUBLIC_KEYRING_FILENAME[32] = "keyring.pub"; -char SECRET_KEYRING_FILENAME[32] = "keyring.sec"; -char RANDSEED_FILENAME[32] = "randseed.pgp"; - -boolean verbose = FALSE; /* -l option: display maximum information */ - -/* -********************************************************************** -*/ - - - -boolean pkzipSignature( byte *header ) -{ - /* - ** Return TRUE if header begins with the PKzip signature - ** Useful for MSDOS only. - */ - - if ((header[0] == 'P') && (header[1] == 'K') - && (header[2] == '\03') && (header[3] == '\04')) - return(TRUE); - return(FALSE); -} /* pkzipSignature */ - - -/* -** Convert to or from external byte order. -** Note that hilo_swap does nothing if this is a LSB-first CPU. -*/ - -#define convert2(x,lx) hilo_swap( (byteptr)&(x), (lx) ) -#define convert(x) convert2( (x), sizeof(x) ) - -word16 fetch_word16(byte *buf) -/* Fetches a 16-bit word from where byte pointer is pointing. - buf points to external-format byteorder array, assuming LSB-first. -*/ -{ word16 w0,w1; - w0 = *buf++; - w1 = *buf++; - return((w1<<8) + w0); -} /* fetch_word16 */ - - -void get_timestamp(byte *timestamp) -/* Returns timestamp byte array, in internal byteorder */ -{ word32 t; - t = time(0); - timestamp[0] = t; /* fill array in external byte order */ - timestamp[1] = t>>8; - timestamp[2] = t>>16; - timestamp[3] = t>>24; - /* Note that hilo_swap does nothing if this is a LSB-first CPU. */ - hilo_swap(timestamp,4); /* convert to internal byteorder */ -} /* get_timestamp */ - - -void CToPascal(char *s) -{ /* "xyz\0" --> "\3xyz" ... converts C string to Pascal string */ - int i,j; - j = string_length(s); - for (i=j; i!=0; i--) - s[i] = s[i-1]; /* move everything 1 byte to the right */ - s[0] = j; /* Pascal length byte at beginning */ -} /* CToPascal */ - - -void PascalToC( char *s ) -{ /* "\3xyz" --> "xyz\0" ... converts Pascal string to C string */ - int i,j; - for (i=0,j=s[0]; i 0L) - { /* write zeros to the whole file... */ - if (flength < (word32) diskbufsize) - count = flength; - else - count = diskbufsize; - fwrite(textbuf,1,count,f); - flength -= count; - } - rewind(f); /* maybe this isn't necessary */ - return(0); /* normal return */ -} /* wipeout */ - - -int wipefile(char *filename) -{ /* Completely overwrite and erase file, so that no sensitive - information is left on the disk. - */ - FILE *f; - /* open file f for read/write, in binary (not text) mode...*/ - if ((f = fopen(filename,"rb+")) == NULL) - return(-1); /* error - file can't be opened */ - wipeout(f); - fclose(f); - return(0); /* normal return */ -} /* wipefile */ - - - -#define strhas(s,c) (strchr((s),(c)) != NULL) - -boolean strhasany( char *s1, char *s2 ) -{ /* Searches s1 for any of the characters in s2. - Returns TRUE if found. - */ - while (*s2) - { if (strhas(s1,*s2)) - return(TRUE); - s2++; - } - return(FALSE); -} /* strhasany */ - - -boolean strcontains( char *s1, char *s2 ) -{ /* - ** Searches s1 for s2, without case sensitivity. - ** Return TRUE if found. - ** - ** If s2 is an empty string then return TRUE. This is because, - ** at least in the world of mathematics, the empty set is contained - ** in all other sets. The Microsoft C version 6.0 strstr function - ** behaves this way but version 5.1 does not, so we need to - ** explicitly test for the situation. -- ALH 91/2/17 - */ - - if (s2[0] != '\0') - { - char buf1[256], buf2[256]; /* scratch buffers */ - - strncpy( buf1, s1, 256 ); strlwr( buf1 ); /* converts to lower case */ - strncpy( buf2, s2, 256 ); strlwr( buf2 ); /* converts to lower case */ - - if (strstr( buf1, buf2 ) == NULL) - return( FALSE ); /* string not found */ - } - return(TRUE); -} /* strcontains */ - - -void translate_spaces(char *s) -/* Changes all the underlines to spaces in a string. */ -{ while (strchr(s,'_') != NULL) - *strchr(s,'_') = ' '; -} - - -boolean no_extension(char *filename) -/* Returns TRUE if user left off file extension, allowing default. */ -{ if (strrchr(filename,'.')==NULL) - return(TRUE); - /* see if the last '.' is followed by a backslash...*/ - if (*(strrchr(filename,'.')+1) == '\\') - return(TRUE); /* just a "..\filename" construct */ - return(FALSE); /* user specified extension, even if a blank one */ -} /* no_extension */ - - -void drop_extension(char *filename) -{ /* deletes trailing ".xxx" file extension after the period. */ - if (!no_extension(filename)) - *strrchr(filename,'.') = '\0'; -} /* drop_extension */ - - -void default_extension(char *filename, char *extension) -{ /* append filename extension if there isn't one already. */ - if (no_extension(filename)) - strcat(filename,extension); -} /* default_extension */ - - -void force_extension(char *filename, char *extension) -{ /* change the filename extension. */ - drop_extension(filename); /* out with the old */ - strcat(filename,extension); /* in with the new */ -} /* force_extension */ - - -boolean getyesno(char default_answer) -{ /* Get yes/no answer from user, returns TRUE for yes, FALSE for no. */ - char buf[8]; - while (keypress()) /* flush typahead buffer */ - getkey(); - getstring(buf,6,TRUE); /* echo keyboard input */ - if (strlen(buf)==0) /* if user didn't give an answer... */ - buf[0] = default_answer; /* assume default answer */ - buf[0] = tolower(buf[0]); - return(buf[0]=='y'); -} /* getyesno */ - - -void maybe_force_extension(char *filename, char *extension) -{ /* if user consents to it, change the filename extension. */ - char newname[64]; - if (!strcontains(filename,extension)) - { strcpy(newname,filename); - force_extension(newname,extension); - if (!file_exists(newname)) - { fprintf(stderr,"\nShould '%s' be renamed to '%s' [Y/n]? ", - filename,newname); - if (getyesno('y')) - rename(filename,newname); - } - } -} /* maybe_force_extension */ - - -/*---------------------------------------------------------------------*/ -/* Begin uuencode routines. - This converts a binary file into printable ASCII characters, in a - form compatible with the Unix uuencode utility. - This makes it easier to send encrypted files over a 7-bit channel. -*/ - -/* ENC is the basic 1 character encoding function to make a char printing */ -#define ENC(c) (((c) & 077) + ' ') - -/* - * output one group of 3 bytes, pointed at by p, on file f. - */ -void outdec(char *p, FILE *f) -{ - int c1, c2, c3, c4; - - c1 = *p >> 2; - c2 = (*p << 4) & 060 | (p[1] >> 4) & 017; - c3 = (p[1] << 2) & 074 | (p[2] >> 6) & 03; - c4 = p[2] & 077; - putc(ENC(c1), f); - putc(ENC(c2), f); - putc(ENC(c3), f); - putc(ENC(c4), f); -} /* outdec */ - - -/* fr: like read but stdio */ -int fr(FILE *fd, char *buf, int cnt) -{ - int c, i; - - for (i=0; i '\137')) - status = -1; /* illegal uudecode character */ - } - for (; n<79; n++) /* when found, fill rest of line with space */ - inbuf[n]=' '; - - inbuf[79]=0; /* terminate new string */ - - checksum = 0; - n = DEC(inbuf[0]); - if (n == 0) - return(0); /* 0 bytes on a line?? Must be the last line */ - - if (status) - return(status); /* bad character, out of range */ - - bp = &inbuf[1]; - - /* FOUR input characters go into each THREE output charcters */ - - while (n >= 4) - { - j = DEC(bp[0]) << 2 | DEC(bp[1]) >> 4; - checksum += j; - outbuf[(*outlength)++]=j; - j = DEC(bp[1]) << 4 | DEC(bp[2]) >> 2; - checksum += j; - outbuf[(*outlength)++]=j; - j = DEC(bp[2]) << 6 | DEC(bp[3]); - checksum += j; - outbuf[(*outlength)++]=j; - checksum = checksum % SUMSIZE; - bp += 4; - n -= 3; - } - - j = DEC(bp[0]) << 2 | DEC(bp[1]) >> 4; - checksum += j; - if (n >= 1) - outbuf[(*outlength)++]=j; - j = DEC(bp[1]) << 4 | DEC(bp[2]) >> 2; - checksum += j; - if (n >= 2) - outbuf[(*outlength)++]=j; - j = DEC(bp[2]) << 6 | DEC(bp[3]); - checksum += j; - if (n >= 3) - outbuf[(*outlength)++]=j; - checksum = checksum % SUMSIZE; - bp += 4; - n -= 3; - - /* The line has been decoded; now check that sum */ - - has_checksum |= !isspace(*bp); - if (has_checksum) /* Is there a checksum at all?? */ - if (checksum != DEC(*bp)) /* Does that checksum match? */ - return(-2); /* checksum error */ - - return(status); /* normal return */ - -} /* uud_buffer */ - -/* - * Copy from in to out, decoding as you go. - * If a return or newline is encountered too early in a line, it is - * assumed that means that some editor has truncated trailing spaces. - */ -int uudecode(FILE *in, FILE *out) -{ -char inbuf[81]; -char outbuf[81]; -char *bp; -boolean has_checksum=FALSE; - -register int j; -int n, status; -int checksum, line; - - for (line = 1; ; line++) /* for each input line */ - { - if (fgets(inbuf, sizeof inbuf, in) == NULL) - { - fprintf(stderr,"ERROR: uudecode input ended unexpectedly!\n"); - return(18); - } - - status = uud_buffer(inbuf,outbuf,&n); - - if (status == -1) - fprintf(stderr,"ERROR: bad uudecode character decoding line %d.\n", line); - if (status == -2) - fprintf(stderr,"ERROR: checksum mismatch decoding line %d.\n", line); - if (n==0) /* zero-length line is the end. */ - break; - - fwrite(outbuf,1,n,out); - - } /* line */ - - return(0); /* normal return */ -} /* uudecode */ - - -boolean is_uufile(char *infile) -{ - FILE *in; - char inbuf[80]; - char outbuf[80]; - int i, n, status; - - if ((in = fopen(infile, "r")) == NULL) - { /* can't open file */ - return(FALSE); - } - - /* search file for header line */ - for (i=0; i<50; i++) /* give up after 50 lines of garbage */ - { - if (fgets(inbuf, sizeof inbuf, in) == NULL) - break; - else - { - if (strncmp(inbuf, "begin ", 6) == 0) - { - if (fgets(inbuf, sizeof inbuf, in) == NULL) - break; - status = uud_buffer(inbuf,outbuf,&n); - if (status < 0) - break; - fclose(in); - return(TRUE); - } - } - } - - fclose(in); - return(FALSE); - -} /* is_uufile */ - - -int uud_file(char *infile, char *outfile) -{ -FILE *in, *out; -int mode; /* file's mode (from header) */ -long filesize; /* theoretical file size (from header) */ -char buf[80]; -int status; - - if ((in = fopen(infile, "r")) == NULL) - { - fprintf(stderr,"ERROR: can't find %s\n", infile); - return(10); - } - - /* Loop through file, searching for header. Decode anything with a - header, complain if there where no headers. */ - - /* search file for header line */ - for (;;) - { - if (fgets(buf, sizeof buf, in) == NULL) - { - fprintf(stderr,"ERROR: no `begin' line!\n"); - fclose(in); - return(12); - } - if (strncmp(buf, "begin ", 6) == 0) - break; - } - - /* Ignore filename and mode. Use outfile instead of dest. */ - /* sscanf(buf, "begin %o %s", &mode, dest); */ - - /* create output file */ - if ((out = fopen(outfile, "wb")) == NULL) - { - fprintf(stderr,"ERROR: can't open output file %s\n", outfile); - fclose(in); - return(15); - } - - status = uudecode(in, out); - if (status != 0) - { fclose(in); - fclose(out); - return(status); - } - - if (fgets(buf, sizeof buf, in) == NULL || strncmp(buf,"end",3)) - { /* don't be overly picky about newline ^ */ - fprintf(stderr,"ERROR: no `end' line\n"); - fclose(in); - fclose(out); - return(16); - } - - if (!(fgets(buf,sizeof buf,in) == NULL || strncmp(buf,"size ",3))) - { - sscanf(buf, "size %ld", &filesize); - if (ftell(out) != filesize) - { - fprintf(stderr,"ERROR: file should have been %ld bytes long but was %ld.\n", filesize, ftell(out)); - return(17); - } - } - fclose(out); - fclose(in); - return(0); /* normal return */ -} /* uud_file */ - - -/* End uudecode routines. */ -/*---------------------------------------------------------------------*/ - - - -boolean legal_ctb(byte ctb) -{ /* Used to determine if nesting should be allowed. */ - boolean legal; - byte ctbtype; - if (!is_ctb(ctb)) /* not even a bonafide CTB */ - 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) - /* || (ctbtype==CTB_CONKEY_TYPE) */ - /* || (ctbtype==CTB_MD_TYPE) */ - ); - return(legal); -} /* legal_ctb */ - - -/*======================================================================*/ - -/* MDfile0(MD, f) -** Computes and returns the message digest from a file position to eof. -** Uses RSA Data Security, Inc. MD4 Message Digest Algorithm. -*/ -int MDfile0(MDstruct *MD, FILE *f) -{ byte X[64]; - int bytecount; - - MDbegin(MD); - /* Process 512 bits, or 64 bytes, at a time... */ - while ((bytecount = fread(X, 1, 64, f)) != 0) - MDupdate(MD, X, bytecount<<3); /* pass bitcount */ - MDupdate(MD, X, 0); /* finish with a bitcount of 0 */ - /* MDprint(MD); */ - - return(0); /* normal return */ -} /* MDfile0 */ - - -/* MDfile(MD, filename) -** Computes and returns the message digest for a specified file. -*/ -int MDfile(MDstruct *MD, char *filename) -{ FILE *f; - f = fopen(filename,"rb"); - if (f == NULL) - { fprintf(stderr,"Can't open file '%s'\n",filename); - return(-1); /* error return */ - } - MDfile0(MD, f); - fclose(f); - return(0); /* normal return */ -} /* MDfile */ - - -/* MD_of_buffer(MD, s, len) -** Computes and returns the message digest for buffer s. -** len is the length in bytes of buffer s. -** Uses RSA Data Security, Inc. MD4 Message Digest Algorithm. -*/ -void MD_of_buffer(MDstruct *MD, byte *s, int len) -{ int i; - - MDbegin(MD); - /* Process 512 bits, or 64 bytes, at a time... */ - for (i=0; i+64<=len; i+=64) - MDupdate(MD, s+i, 512); - MDupdate(MD, s+i, (len-i)<<3); /* finish with short block */ - /* MDprint(MD); */ - -} /* MD_of_buffer */ - - -boolean equal_buffers(byte *buf1, byte *buf2, word16 count) -/* Compares two byte buffers. */ -{ while (count--) - if (*buf1++ != *buf2++) - return(FALSE); /* mismatch. */ - return(TRUE); /* compares OK */ -} /* equal_buffers */ - - -char *buildfilename(char *result, char *fname) -/* Builds a filename with a complete path specifier from the environmental - variable PGPPATH. Assumes MSDOS pathname conventions. -*/ -{ char *s = getenv(PGPPATH); - if (strlen(s) > 50) /* too long to use */ - s = ""; - strcpy(result,s); - if (strlen(result) != 0) - if (result[strlen(result)-1] != '\\') - strcat(result,"\\"); - strcat(result,fname); - return(result); -} /* buildfilename */ - - -int strong_pseudorandom(byte *buf, int bufsize) -/* Reads BassOmatic random key and random number seed from file, - cranks the the seed through the bassrand 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 BassOmatic - 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[64]; /* Random seed filename */ - FILE *f; - byte key[64]; /* not to exceed 256 byes in length */ - byte seed[256]; /* not to exceed 256 byes in length */ - 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(stderr,"Initializing random seed file..."); - randaccum(8*(sizeof(key)+32)); - for (i=1; i0) - { fwrite(textbuf,1,count,g); - longcount -= count; - } - /* if text block was short, exit loop */ - } while (count==diskbufsize); - burn(textbuf); /* burn sensitive data on stack */ -} /* copyfile */ - - -word32 getpastlength(byte ctb, FILE *f) -/* Returns the length of a packet according to the CTB and - the length field. */ -{ word32 length; - int llength; /* length of length */ - byte buf[8]; - - fill0(buf,sizeof(buf)); - length = 0L; - /* Use ctb length-of-length field... */ - llength = ctb_llength(ctb); /* either 1, 2, 4, or 8 */ - if (llength==8) /* 8 means no length field, assume huge length */ - return(-1L); /* return huge length */ - - /* now read in the actual length field... */ - if (fread((byteptr) buf,1,llength,f) < llength) - return (-2L); /* error -- read failure or premature eof */ - /* convert length from external LSB-first format... */ - while (llength--) - { length <<= 8; - length += buf[llength]; - } - return(length); -} /* getpastlength */ - - - -int bass_file(byte *basskey, int lenbasskey, boolean decryp, - FILE *f, FILE *g) -/* Use BassOmatic in cipher feedback (CFB) mode to encrypt - or decrypt a file. Encrypted key check bytes determine - if correct BassOmatic key was used to decrypt ciphertext. -*/ -{ int count; - byte textbuf[diskbufsize], iv[256]; -#define KEYCHECKLENGTH 4 - - /* init CFB BassOmatic key */ - fill0(iv,256); /* define initialization vector IV as 0 */ - if ( initcfb(iv,basskey,lenbasskey,decryp) < 0 ) - return(-1); /* Error return should be impossible. */ - - if (!decryp) /* encrypt-- insert key check bytes */ - { /* key check bytes are 2 copies of 16 random bits */ - textbuf[0] = randombyte(); - textbuf[1] = randombyte(); - textbuf[2] = textbuf[0]; - textbuf[3] = textbuf[1]; - basscfb(textbuf,KEYCHECKLENGTH); - fwrite(textbuf,1,KEYCHECKLENGTH,g); - } - else /* decrypt-- check for key check bytes */ - { /* See if there are 2 copies of 16 random bits */ - count = fread(textbuf,1,KEYCHECKLENGTH,f); - if (count==KEYCHECKLENGTH) - { basscfb(textbuf,KEYCHECKLENGTH); - if ((textbuf[0] != textbuf[2]) - || (textbuf[1] != textbuf[3])) - { return(-2); /* bad key error */ - } - } - else /* file too short for key check bytes */ - return(-3); /* error of the weird kind */ - } - - - do /* read and write the whole file in CFB mode... */ - { count = fread(textbuf,1,diskbufsize,f); - if (count>0) - { basscfb(textbuf,count); - fwrite(textbuf,1,count,g); - } - /* if text block was short, exit loop */ - } while (count==diskbufsize); - - closebass(); /* release BassOmatic resources */ - burn(textbuf); /* burn sensitive data on stack */ - return(0); /* should always take normal return */ -} /* bass_file */ - - - -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 count; - word16 bytecount,bitcount,lowcount,highcount; - - mp_init(r,0); - - if ((count = fread(buf,1,2,f)) < 2) - return (-1); /* error -- read failure or premature eof */ - - /* Assumes external format is LSB-first */ - bitcount = (((word16) buf[1]) << 8) + (word16) buf[0]; - if (bits2units(bitcount) > global_precision) - return(-1); /* error -- possible corrupted bitcount */ - - bytecount = bits2bytes(bitcount); - - count = fread(buf+2,1,bytecount,f); - if (count < bytecount) - return(-1); /* error -- premature eof */ - - if (scrambled) /* decrypt the field */ - basscfb(buf+2,bytecount); - - /* We assume that the bitcount prefix we read is an exact - bitcount, not rounded up to the next byte boundary. - Otherwise we would have to call mpi2reg, then call - countbits, then call set_precision, then recall mpi2reg - again. - */ - if (adjust_precision && bytecount) - { /* set the precision to that specified by the number read. */ - set_precision(bits2units(bitcount+SLOP_BITS)); - /* Now that precision is optimally set, call mpi2reg */ - } - - mpi2reg(r,buf); /* convert to internal format */ - 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]; - short bytecount; - bytecount = reg2mpi(buf,n); - if (scrambled) /* encrypt the field, skipping over the bitcount */ - basscfb(buf+2,bytecount); - fwrite(buf,1,bytecount+2,f); - burn(buf); /* burn sensitive data on stack */ -} /* write_mpi */ - - - -void showkeyID(byte *buf) -/* Print key fragment, which is an abbreviation or "fingerprint" - of the key. - Show LEAST significant 64 bits (KEYFRAGSIZE bytes) of modulus, - LSB last. Yes, that's LSB LAST. -*/ -{ short i,j; - /* fputc('[',stderr); */ - j = KEYFRAGSIZE; - for (i=KEYFRAGSIZE-1; i>=0; i--) /* print LSB last */ - { if (--j < 3) /* only show bottom 3 bytes of keyID */ - fprintf(stderr,"%02X",buf[i]); - } - /* fputc(']',stderr); */ -} /* showkeyID */ - - - -void extract_keyID(byteptr keyID, unitptr n) -/* Extract key fragment from modulus n. keyID byte array must be - at least KEYFRAGSIZE bytes long. -*/ -{ byte buf[MAX_BYTE_PRECISION+2]; - short i, j; - - fill0(buf,KEYFRAGSIZE+2); /* in case n is too short */ - reg2mpi(buf,n); /* MUST be at least KEYFRAGSIZE long */ - /* For low-byte-first keyID format, start of keyID is: */ - i = 2; /* skip over the 2 bytes of bitcount */ - for (j=0; j MAX_KEYCERT_LENGTH-3) - return(-3); /* bad length */ - - fread(timestamp,1,4,f); /* read certificate timestamp */ - /* note that hilo_swap does nothing if this is a LSB-first CPU */ - hilo_swap(timestamp,4); /* convert from external LSB-first form */ - count = fread(userid,1,1,f); /* read user ID length byte */ - if (count==0) return(-1); /* premature eof */ - fread(userid+1,1,userid[0],f); /* read rest of user ID */ - /*** End certificate header fields ***/ - - /* We're past certificate headers, now look at some key material...*/ - - if (read_mpi(n,f,TRUE,FALSE) < 0) - return(-4); /* data corrupted, return error */ - - /* Note that precision was adjusted for n */ - - if (read_mpi(e,f,FALSE,FALSE) < 0) - return(-4); /* data corrupted, error return */ - - cert_length -= SIZEOF_TIMESTAMP + userid[0]+1 + - (countbytes(n)+2) + (countbytes(e)+2); - - if (d==NULL) /* skip rest of this key certificate */ - { fseek(f, cert_length, SEEK_CUR); - cert_length = 0; /* because we are skipping secret fields */ - } - else /* d is not NULL */ - { if (is_secret_key(ctb)) - { - if (read_mpi(d,f,FALSE,hidekey) < 0) - return(-4); /* data corrupted, error return */ - if (read_mpi(p,f,FALSE,hidekey) < 0) - return(-4); /* data corrupted, error return */ - if (read_mpi(q,f,FALSE,hidekey) < 0) - return(-4); /* data corrupted, error return */ - - /* use register 'u' briefly as scratchpad */ - mp_mult(u,p,q); /* compare p*q against n */ - if (mp_compare(n,u)!=0) /* bad pass phrase? */ - return(-5); /* possible bad pass phrase, error return */ - /* now read in real u */ - if (read_mpi(u,f,FALSE,hidekey) < 0) - return(-4); /* data corrupted, error return */ - - cert_length -= (countbytes(d)+2) + (countbytes(p)+2) - + (countbytes(q)+2) + (countbytes(u)+2); - - } /* secret key */ - else /* not a secret key */ - { mp_init(d,0); - mp_init(p,0); - mp_init(q,0); - mp_init(u,0); - } - } /* d != NULL */ - - if (cert_length != 0) - { fprintf(stderr,"\n\aCorrupted key. Bad length, off by %d bytes.\n", - (signed int) cert_length); - return(-4); /* data corrupted, error return */ - } - - return(0); /* normal return */ - -} /* readkeypacket */ - - - -int getpublickey(boolean giveup, boolean showkey, char *keyfile, - long *file_position, int *pktlen, byte *keyID, byte *timestamp, - byte *userid, unitptr n, unitptr e) -/* keyID contains key fragment we expect to find in keyfile. - If keyID is NULL, then userid contains a C string search target of - userid to find in keyfile. - keyfile is the file to begin search in, and it may be modified - to indicate true filename of where the key was found. It can be - either a public key file or a secret key file. - file_position is returned as the byte offset within the keyfile - that the key was found at. - giveup is TRUE iff we are just going to do a single file search only. -*/ -{ - int keytype; /* 1 for secret key, 0 for public key */ - byte ctb; /* returned by readkeypacket */ - FILE *f; - int status; - boolean keyfound = FALSE; - boolean secret; /* indicates we are called by getsecretkey */ - char userid0[256]; /* C string format */ - - userid0[0] = '\0'; - secret = strcontains(keyfile,SEC_EXTENSION); - - if (keyID==NULL) /* then userid has search target */ - strcpy(userid0,userid); - -top: - if (secret) - default_extension(keyfile,SEC_EXTENSION); - else - default_extension(keyfile,PUB_EXTENSION); - - if (!file_exists(keyfile)) - { if (giveup) - return(-1); /* give up, error return */ - fprintf(stderr,"\nKeyring file '%s' does not exist. ",keyfile); - goto nogood; - } - if (verbose) fprintf(stderr,"\nSearching key ring file '%s'.\n",keyfile); - - /* open file f for read, in binary (not text) mode...*/ - if ((f = fopen(keyfile,"rb")) == NULL) - return(-1); /* error return */ - - while (TRUE) - { - *file_position = ftell(f); - status = readkeypacket(f,FALSE,&ctb,timestamp,userid,n,e, - NULL,NULL,NULL,NULL); - /* Note that readkeypacket has called set_precision */ - - if (status == -1) /* end of file */ - break; - - if (status < -1) - { fprintf(stderr,"\n\aCould not read key from file '%s'.\n", - keyfile); - fclose(f); /* close key file */ - return(-1); - } - - /* keyID contains key fragment. Check it against n from keyfile. */ - if (keyID!=NULL) - keyfound = checkkeyID(keyID,n); - else - { /* userid0 is already a C string */ - PascalToC(userid); /* for C string functions */ - keyfound = strcontains(userid,userid0); /* any matching subset? */ - /* keyfound = (strcmp(userid,userid0)==0); /* exact match? */ - CToPascal(userid); - } - - if (keyfound) - { *pktlen = (ftell(f) - *file_position); - if (showkey) - { PascalToC(userid); /* for display */ - fprintf(stderr,"\nKey for user ID: %s\n",userid); - CToPascal(userid); - fprintf(stderr,"%d-bit key, Key ID ",countbits(n)); - showkeyID2(n); - fprintf(stderr,", created %s",ctime((long *)timestamp)); - } - fclose(f); - return(0); /* normal return */ - } - } /* while TRUE */ - - fclose(f); /* close key file */ - - if (giveup) - return(-1); /* give up, error return */ - - if (keyID!=NULL) - { - fprintf(stderr,"\n\aKey matching expected Key ID "); - showkeyID(keyID); - fprintf(stderr," not found in file '%s'.\n",keyfile); - } - else - { fprintf(stderr,"\n\aKey matching userid '%s' not found in file '%s'.\n", - userid0,keyfile); - } - -nogood: - if (giveup) - return(-1); /* give up, error return */ - - if (secret) - fprintf(stderr,"Enter secret key filename: "); - else - fprintf(stderr,"Enter public key filename: "); - - getstring(keyfile,59,TRUE); /* echo keyboard input */ - if (strlen(keyfile) > 0) - goto top; - - return(-1); /* give up, error return */ - -} /* getpublickey */ - - - -int getsecretkey(byte *keyID, byte *timestamp, byte *userid, - unitptr n, unitptr e, unitptr d, unitptr p, unitptr q, unitptr u) -/* keyID contains key fragment we expect to find in keyfile. - If keyID is NULL, then userid contains search target of - userid to find in keyfile. -*/ -{ - byte ctb; /* returned by readkeypacket */ - FILE *f; - char keyfile[64]; /* for getpublickey */ - long file_position; - int pktlen; /* unused, just to satisfy getpublickey */ - int status; - boolean hidekey = FALSE; /* TRUE iff secret key is encrypted */ - char passphrase[256]; - byte iv[256]; /* for BassOmatic CFB mode */ - int guesses = 3; - - buildfilename(keyfile,SECRET_KEYRING_FILENAME); /* use default pathname */ - - status = getpublickey(FALSE, TRUE, keyfile, &file_position, &pktlen, - keyID, timestamp, userid, n, e); - if (status < 0) - return(status); /* error return */ - - /* open file f for read, in binary (not text) mode...*/ - if ((f = fopen(keyfile,"rb")) == NULL) - return(-1); /* error return */ - - /* First guess is null password, so hidekey is FALSE */ - - do /* until good password */ - { /* init CFB BassOmatic key */ - if (hidekey) - { fill0(iv,256); /* define initialization vector IV as 0 */ - if ( initcfb(iv,passphrase,string_length(passphrase),TRUE) < 0 ) - { fclose(f); /* close key file */ - return(-1); - } - } - burn(passphrase); /* burn sensitive data on stack */ - fseek(f,file_position,SEEK_SET); /* reposition file to key */ - status = readkeypacket(f,hidekey,&ctb,timestamp,userid,n,e,d,p,q,u); - if (hidekey) - closebass(); /* release BassOmatic resources */ - - if (status == -5) /* bad pass phrase status */ - { if (guesses!=3) /* not first guess of null password? */ - fprintf(stderr,"\n\aUnreadable secret key. Possible bad pass phrase.\n"); - if (--guesses) /* not ran out of guesses yet */ - { fprintf(stderr,"\nYou need a pass phrase to unlock your RSA secret key. "); - hidekey = (getpassword(passphrase,1,0x0f) > 0); - continue; /* take it from the top */ - } /* more guesses to go */ - } - if (status < 0) - { fprintf(stderr,"\n\aCould not read key from file '%s'.\n", - keyfile); - fclose(f); /* close key file */ - return(-1); - } - } while (status < 0); /* until key reads OK, with good password */ - - fclose(f); /* close key file */ - - if (!hidekey) - fprintf(stderr,"\nAdvisory warning: This RSA secret key is not protected by a passphrase.\n"); - else - fprintf(stderr,"Pass phrase is good. "); - - /* Note that readkeypacket has called set_precision */ - - if (testeq(d,0)) /* didn't get secret key components */ - { fprintf(stderr,"\n\aKey file '%s' is not a secret key file.\n",keyfile); - return(-1); - } - - return(0); /* normal return */ - -} /* getsecretkey */ - - - -int make_signature_certificate(byte *certificate, MDstruct *MD, - byte *userid, unitptr n, unitptr d, unitptr p, unitptr q, unitptr u) -/* 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[32]; - byte *mdbufptr; - int i,j,certificate_length,blocksize,bytecount; - word16 useridlength,certsig_length,mdp_length,ske_length; - word32 tstamp; byte *timestamp = (byte *) &tstamp; - byte keyID[KEYFRAGSIZE]; - - /* 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(stderr,"\n\aError: RSA key length must be at least 256 bits.\n"); - return(-1); - } - - get_timestamp(timestamp); /* Timestamp when signature was made */ - hilo_swap(timestamp,4); /* convert to external LSB-first form */ - - fill0(mdpacket,sizeof(mdpacket)); - mdpacket[0] = CTB_MD; /* Message Digest type */ - /* mdp_length includes algorithm byte, MD, and timestamp. */ - mdp_length = 1+16+4; /* message digest packet length */ - /* MD packet length does not include itself or CTB prefix: */ - mdpacket[1] = mdp_length; - mdpacket[2] = MD4_ALGORITHM_BYTE; /* select MD4 algorithm */ - - mdbufptr = (byte *) (MD->buffer); /* point at actual message digest */ - for (i=0; i<16; i++) - mdpacket[i+3] = *mdbufptr++; /* Assumes LSB-first order */ - /* Stick a timestamp in here, before signing... */ - /* timestamp already in external format */ - for (j=0; j> 8) & 0xff; - - /* Now append keyID... */ - extract_keyID(keyID, n); /* gets keyID */ - for (i=0; i MAX_SIGCERT_LENGTH-3) - { fprintf(stderr,"\n\aSignature file '%s' has huge packet length field.\n",infile); - goto err1; - } - - /* read whole certificate: */ - if (fread((byteptr) certificate, 1, cert_length, f) < cert_length) - { fprintf(stderr,"\n\aSignature file '%s' has bad packet length field.\n",infile); - goto err1; - } - - if (!is_ctb_type(ctb,CTB_SKE_TYPE)) - { fprintf(stderr,"\n\a'%s' is not a signature file.\n",infile); - goto err1; - } - - for (i=0; i 0) - fprintf(stderr,", looking for user ID \"%s\".",mcguffin); - fprintf(stderr,"\nType bits/keyID Date User ID\n"); - do - { - status = readkeypacket(f,FALSE,&ctb,timestamp,userid,n,e, - NULL,NULL,NULL,NULL); - /* Note that readkeypacket has called set_precision */ - if (status== -1 ) break; /* eof reached */ - if (status < 0) - { fprintf(stderr,"\n\aCould not read key from file '%s'.\n", - ringfile); - fclose(f); /* close key file */ - return(-1); - } - - if (!is_ctb_type(ctb,CTB_CERT_PUBKEY_TYPE) - && !is_ctb_type(ctb,CTB_CERT_SECKEY_TYPE)) - { - fprintf(stderr,"\n\aError in file '%s'. Not a key certificate.\n", - ringfile); - return(-1); - } - - keycounter++; - - extract_keyID(keyID, n); - PascalToC(userid); - - if (strcontains(userid,mcguffin)) - { - if (is_ctb_type(ctb,CTB_CERT_PUBKEY_TYPE)) - { - if (testeq(e,0)) /* e==0 means key compromised */ - fprintf(stderr,"com "); /* "key compromised" certificate */ - else - fprintf(stderr,"pub "); /* public key certificate */ - } - else if (is_ctb_type(ctb,CTB_CERT_SECKEY_TYPE)) - fprintf(stderr,"sec "); /* secret key certificate */ - else - fprintf(stderr,"??? "); /* otherwise, who knows? */ - - fprintf(stderr,"%4d/",countbits(n)); - showkeyID(keyID); - fputc(' ',stderr); - show_date((long *)timestamp); - fprintf(stderr," "); - fprintf(stderr,userid); - fputc('\n',stderr); - } /* if it has mcguffin */ - } while (status >= 0); - - fclose(f); /* close key file */ - fprintf(stderr,"%d key(s) examined. ",keycounter); - - return(0); /* normal return */ - -} /* view_keyring */ - - - -int remove_from_keyring(byte *keyID, char *mcguffin, char *ringfile) -/* Remove the first entry in key ring that has mcguffin string in userid. - Or it removes the first matching keyID from the ring. - A non-NULL keyID takes precedence over a mcguffin specifier. - mcguffin is a null-terminated C string. -*/ -{ - FILE *f; - FILE *g; - long file_position,fp,after_key; - int packetlength=0; - byte ctb; - int status; - unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; - byte userid[256]; /* key certificate userid */ - word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ - - default_extension(ringfile,PUB_EXTENSION); - - if ((keyID==NULL) && (strlen(mcguffin)==0)) - return(-1); /* error, null mcguffin will match everything */ - - strcpy(userid,mcguffin); - - fprintf(stderr,"\nRemoving from key ring: '%s'",ringfile); - if (strlen(mcguffin) > 0) - fprintf(stderr,", userid \"%s\".\n",mcguffin); - - status = getpublickey(TRUE, TRUE, ringfile, &fp, &packetlength, NULL, timestamp, userid, n, e); - if (status < 0) - { fprintf(stderr,"\n\aKey not found in key ring '%s'.\n",ringfile); - return(0); /* normal return */ - } - after_key = fp + packetlength; - - if (testeq(e,0)) /* This is a key compromise certificate. */ - { /* Wish there was a more elegant way to handle this... */ - fprintf(stderr,"\n\aWARNING: This is a \"key compromised\" certificate."); - fprintf(stderr,"\nIt should not be removed from the key ring!\n"); - if (keyID != NULL) /* Decision requires human confirmation. */ - return(-1); - } - - if (keyID==NULL) /* Human confirmation is required. */ - { /* Supposedly the key was fully displayed by getpublickey */ - fprintf(stderr,"\nAre you sure you want this key removed (y/N)? "); - if (!getyesno('n')) - return(-1); /* user said "no" */ - } - - /* open file f for read, in binary (not text) mode...*/ - if ((f = fopen(ringfile,"rb")) == NULL) - { fprintf(stderr,"\n\aCan't open key ring file '%s'\n",ringfile); - return(-1); - } - - remove(SCRATCH_KEYRING_FILENAME); - /* open file g for writing, in binary (not text) mode...*/ - if ((g = fopen(SCRATCH_KEYRING_FILENAME,"wb")) == NULL) - { fclose(f); - return(-1); - } - rewind(f); - copyfile(f,g,fp); /* copy file f to g up to position fp */ - fseek(f,after_key,SEEK_SET); /* reposition file to after key */ - copyfile(f,g,-1UL); /* copy rest of file from file f to g */ - fclose(g); /* close scratch file */ - fclose(f); /* close key file */ - remove(ringfile); /* dangerous. sure hope rename works... */ - rename(SCRATCH_KEYRING_FILENAME,ringfile); - fprintf(stderr,"\nKey removed from key ring. "); - - return(0); /* normal return */ - -} /* remove_from_keyring */ - - - -int addto_keyring(char *keyfile, char *ringfile) -/* Adds (prepends) key file to key ring file */ -{ FILE *f; - FILE *g; - long file_position,fp; - int pktlen; /* unused, just to satisfy getpublickey */ - byte ctb; - int status; - unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION]; - byte keyID[KEYFRAGSIZE]; - byte userid[256]; /* key certificate userid */ - word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ - boolean keycompromised; - - if (strcontains(ringfile,SEC_EXTENSION)) - force_extension(SCRATCH_KEYRING_FILENAME,SEC_EXTENSION); - else - force_extension(SCRATCH_KEYRING_FILENAME,PUB_EXTENSION); - - /* open file f for read, in binary (not text) mode...*/ - if ((f = fopen(keyfile,"rb")) == NULL) - { fprintf(stderr,"\n\aCan't open key file '%s'\n",keyfile); - return(-1); - } - - /* Check to see if the keyID is already in key ring before we add it in. */ - - file_position = ftell(f); - status = readkeypacket(f,FALSE,&ctb,timestamp,userid,n,e, - NULL,NULL,NULL,NULL); - /* Note that readkeypacket has called set_precision */ - if (status < 0) - { fprintf(stderr,"\n\aCould not read key from file '%s'.\n", - keyfile); - fclose(f); /* close key file */ - return(-1); - } - - if (!is_ctb_type(ctb,CTB_CERT_PUBKEY_TYPE) - && !is_ctb_type(ctb,CTB_CERT_SECKEY_TYPE)) - { fprintf(stderr,"\n\aError in file '%s'. Not a key certificate.\n", - keyfile); - return(-1); - } - - extract_keyID(keyID, n); /* from keyfile, not ringfile */ - - if (!file_exists(ringfile)) - { /* ringfile does not exist. Can it be created? */ - /* open file g for writing, in binary (not text) mode...*/ - g = fopen(ringfile,"wb"); - if (g==NULL) - { fprintf(stderr,"\n\aKey ring file '%s' cannot be created.\n",ringfile); - fclose(f); - return(-1); - } - fclose(g); - } - - /* See if we are adding a "secret key compromised" certificate: */ - keycompromised = testeq(e,0); - - /* If this is a key compromise certificate, maybe we should - remove the real public key from the key ring if it's on the - key ring before adding the key compromise certificate. - Probably not, though, because the prepended key compromise - certificate will take search order precedence. - And it may be nice to keep the original public key certificate - around for its timestamp, to check old signatures. - It should not be possible to later add the same public key to - the ring again if the key compromise certificate was there first. - - These tests for duplicates should have to be applied for all - the keys being added to the ring, in case the added key file - is itself a multikey ring. Fix this later. - */ - - /* Check for duplicate key in key ring: */ - if (getpublickey(TRUE, TRUE, ringfile, &fp, &pktlen, keyID, timestamp, userid, n, e) >= 0) - { fprintf(stderr,"\n\aKey already included in key ring '%s'.\n",ringfile); - if (!keycompromised) /* allows duplicate if key compromised */ - { fclose(f); /* close key file */ - return(0); /* normal return */ - } - } - - if (keycompromised) - fprintf(stderr,"\nAdding \"key compromise\" certificate '%s' to key ring '%s'.\n", - keyfile,ringfile); - else - fprintf(stderr,"\nAdding key certificate '%s' to key ring '%s'.\n",keyfile,ringfile); - - /* The key is prepended to the ring to give it search precedence - over other keys with that same userid. */ - - fseek(f,file_position,SEEK_SET); /* reposition file to key */ - - remove(SCRATCH_KEYRING_FILENAME); - /* open file g for writing, in binary (not text) mode...*/ - if ((g = fopen(SCRATCH_KEYRING_FILENAME,"wb")) == NULL) - { fclose(f); - return(-1); - } - copyfile(f,g,-1UL); /* copy rest of file from file f to g */ - fclose(f); - - - /* open file f for reading, in binary (not text) mode...*/ - if ((f = fopen(ringfile,"rb")) != NULL) - { copyfile(f,g,-1UL); /* copy rest of file from file f to g */ - fclose(f); - } - fclose(g); - - remove(ringfile); /* dangerous. sure hope rename works... */ - rename(SCRATCH_KEYRING_FILENAME,ringfile); - - return(0); /* normal return */ - -} /* addto_keyring */ - - -/*======================================================================*/ - - - -int dokeygen(char *keyfile, char *numstr, char *numstr2) -/* Do an RSA key pair generation, and write them out to a pair of files. - The keyfile filename string must not have a file extension. - numstr is a decimal string, the desired bitcount for the modulus n. - numstr2 is a decimal string, the desired bitcount for the exponent e. -*/ -{ unit n[MAX_UNIT_PRECISION], e[MAX_UNIT_PRECISION], d[MAX_UNIT_PRECISION], - p[MAX_UNIT_PRECISION], q[MAX_UNIT_PRECISION], u[MAX_UNIT_PRECISION]; - char fname[64]; - char ringfile[64]; - byte iv[256]; /* for BassOmatic CFB mode, to protect RSA secret key */ - byte userid[256]; - short keybits,ebits,i; - word32 tstamp; byte *timestamp = (byte *) &tstamp; /* key certificate timestamp */ - boolean hidekey; /* TRUE iff secret key is encrypted */ - - strcpy(fname,keyfile); - if (strlen(fname)==0) - { fprintf(stderr,"\nKey file name is required for RSA key pair: "); - getstring(fname,sizeof(fname)-4,TRUE); - } - - if (strlen(numstr)==0) - { fprintf(stderr,"\nPick your RSA key size: " - "\n 1) 288 bits- Casual grade, fast but less secure" - "\n 2) 512 bits- Commercial grade, medium speed, good security" - "\n 3) 992 bits- Military grade, very slow, highest security" - "\nChoose 1, 2, or 3, or enter desired number of bits: "); - numstr = userid; /* use userid buffer as scratchpad */ - getstring(numstr,5,TRUE); /* echo keyboard */ - } - - keybits = 0; - while ((*numstr>='0') && (*numstr<='9')) - keybits = keybits*10 + (*numstr++ - '0'); - - /* Standard default key sizes: */ - if (keybits==1) keybits=286; /* Casual grade */ - if (keybits==2) keybits=510; /* Commercial grade */ - if (keybits==3) keybits=990; /* Military grade */ - - /* minimum RSA keysize for BassOmatic bootstrap: */ - if (keybits<286) keybits=286; - - ebits = 0; /* number of bits in e */ - while ((*numstr2>='0') && (*numstr2<='9')) - ebits = ebits*10 + (*numstr2++ - '0'); - - fprintf(stderr,"\nGenerating an RSA key with a %d-bit modulus... ",keybits); - - fprintf(stderr,"\nEnter a user ID for your public key (your name): "); - getstring(userid,255,TRUE); /* echo keyboard input */ - CToPascal(userid); /* convert to length-prefixed string */ - - { char passphrase[256]; - fprintf(stderr,"\nYou need a pass phrase to protect your RSA secret key. "); - hidekey = (getpassword(passphrase,2,0x0f) > 0); - /* init CFB BassOmatic key */ - if (hidekey) - { fill0(iv,256); /* define initialization vector IV as 0 */ - if ( initcfb(iv,passphrase,string_length(passphrase),FALSE) < 0 ) - return(-1); - burn(passphrase); /* burn sensitive data on stack */ - } - } - - fprintf(stderr,"\nNote that key generation is a VERY lengthy process.\n"); - - if (keygen(n,e,d,p,q,u,keybits,ebits) < 0) - { fprintf(stderr,"\n\aKeygen failed!\n"); - return(-1); /* error return */ - } - - if (verbose) - { - fprintf(stderr,"Key ID "); - showkeyID2(n); fputc('\n',stderr); - - mp_display(" modulus n = ",n); - mp_display("exponent e = ",e); - - mp_display("exponent d = ",d); - mp_display(" prime p = ",p); - mp_display(" prime q = ",q); - mp_display(" inverse u = ",u); - } - - get_timestamp(timestamp); /* Timestamp when key was generated */ - - fputc('\a',stderr); /* sound the bell when done with lengthy process */ - - force_extension(fname,SEC_EXTENSION); - writekeyfile(fname,hidekey,timestamp,userid,n,e,d,p,q,u); - force_extension(fname,PUB_EXTENSION); - writekeyfile(fname,FALSE,timestamp,userid,n,e,NULL,NULL,NULL,NULL); - - if (hidekey) /* done with Bassomatic to protect RSA secret key */ - closebass(); - - mp_burn(d); /* burn sensitive data on stack */ - mp_burn(p); /* burn sensitive data on stack */ - mp_burn(q); /* burn sensitive data on stack */ - mp_burn(u); /* burn sensitive data on stack */ - mp_burn(e); /* burn sensitive data on stack */ - mp_burn(n); /* burn sensitive data on stack */ - burn(iv); /* burn sensitive data on stack */ - - force_extension(fname,PUB_EXTENSION); - buildfilename(ringfile,PUBLIC_KEYRING_FILENAME); - fprintf(stderr,"\nAdd public key to key ring '%s' (y/N)? ",ringfile); - if (getyesno('n')) - addto_keyring(fname,ringfile); - force_extension(fname,SEC_EXTENSION); - buildfilename(ringfile,SECRET_KEYRING_FILENAME); - fprintf(stderr,"Add secret key to key ring '%s' (y/N)? ",ringfile); - if (getyesno('n')) - addto_keyring(fname,ringfile); - - /* Force initialization of cryptographically strong pseudorandom - number generator seed file for later use... - */ - strong_pseudorandom(iv,1); - - return(0); /* normal return */ -} /* dokeygen */ - - -/*======================================================================*/ - - -void main(int argc, char *argv[]) -{ char keyfile[64], plainfile[64], cipherfile[64], ringfile[64], tempfile[64]; - int status,i; - boolean nestflag = FALSE; - boolean uu_emit = FALSE; - boolean wipeflag = FALSE; - byte ctb; - byte header[6]; /* used to classify file type at the end. */ - char mcguffin[256]; /* userid search tag */ - -#ifdef DEBUG1 - verbose = TRUE; -#endif - - fprintf(stderr,"Pretty Good Privacy 1.0 - RSA public key cryptography for the masses.\n" - "(c) Copyright 1990 Philip Zimmermann, Phil's Pretty Good Software. 5 Jun 91\n"); - - if (argc <= 1) - { fprintf(stderr, - "\nFor details on free licensing and distribution, see the PGP User's Guide." - "\nFor other cryptography products and custom development services, contact:" - "\nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, phone (303)444-4541" - ); - goto usage; - } - - /* Make sure arguments will fit into filename strings: */ - for (i = 1; i <= argc; i++) - { - if (strlen(argv[i]) >= sizeof(cipherfile)-4) - { - fprintf(stderr, "\aInvalid filename: [%s] too long\n", argv[i] ); - goto user_error; - } - } - - if (argv[1][0] == '-') - { - if (strhas(argv[1],'l')) - verbose = TRUE; - - nestflag = strhas(argv[1],'n'); - - uu_emit = strhas(argv[1],'u'); - - wipeflag = strhas(argv[1],'w'); - - /*-------------------------------------------------------*/ - if ( (argc >= 3) - && strhasany(argv[1],"sS") && strhasany(argv[1],"eE") ) - { /* Sign AND encrypt file */ - /* Arguments: plainfile, her_userid, your_userid, cipherfile */ - boolean separate_signature = FALSE; - - strcpy( plainfile, argv[2] ); - - if (argc>=6) /* default signature file extension */ - { strcpy( cipherfile, argv[5] ); - default_extension( cipherfile, CTX_EXTENSION ); - } - else - { /* Default the signature file name */ - strcpy( cipherfile, plainfile ); - /* ...but replace file extension: */ - force_extension( cipherfile, CTX_EXTENSION ); - } - - if (strcmp( plainfile, cipherfile ) == 0) - { fprintf(stderr, "\aFile [%s] must be specified just once.\n", plainfile ); - goto user_error; /* same filenames for both files */ - } - - if (argc>=5) - { strcpy( mcguffin, argv[4] ); /* Userid of signer */ - translate_spaces( mcguffin ); /* change all '_' to ' ' */ - } - else - { fprintf(stderr, "\nEnter userid to look up your secret key for signature: "); - getstring( mcguffin, 255, TRUE ); /* echo keyboard */ - } - - if (nestflag) /* user thinks this file has nested info */ - { get_header_info_from_file( plainfile, &ctb, 1); - if (!legal_ctb(ctb)) - { nestflag = FALSE; - fprintf(stderr,"\n\aNo nestable data in plaintext file '%s'.\n",plainfile); - } - } - - status = signfile( nestflag, separate_signature, - mcguffin, plainfile, SCRATCH_CTX_FILENAME ); - - if (status < 0) /* signfile failed */ - { fprintf(stderr, "\aSignature error\n" ); - goto user_error; - } - - if (wipeflag) - { wipefile(plainfile); /* destroy every trace of plaintext */ - remove(plainfile); - fprintf(stderr,"\nFile %s wiped and deleted. ",plainfile); - } - - if (argc>=4) - { strcpy( mcguffin, argv[3] ); /* Userid of recipient */ - translate_spaces( mcguffin ); /* change all '_' to ' ' */ - } - else - { fprintf(stderr, "\nEnter userid to look up recipient's public key: "); - getstring( mcguffin, 255, TRUE ); /* echo keyboard */ - } - - /* Indicate that encrypted data has nested signature: */ - - status = encryptfile( TRUE, mcguffin, SCRATCH_CTX_FILENAME, cipherfile ); - - wipefile( SCRATCH_CTX_FILENAME ); - remove( SCRATCH_CTX_FILENAME ); - - if (status < 0) - { fprintf(stderr, "\aEncryption error\n" ); - goto user_error; - } - - if (uu_emit) - { status = uue_file(cipherfile, SCRATCH_CTX_FILENAME); - remove(cipherfile); /* dangerous. sure hope rename works... */ - rename(SCRATCH_CTX_FILENAME, cipherfile); - } - - if (!verbose) /* if other filename messages were supressed */ - fprintf(stderr,"\nCiphertext file: %s ", cipherfile); - - exit(0); - } /* Sign AND encrypt file */ - - - /*-------------------------------------------------------*/ - if ( (argc >= 3) && strhasany(argv[1],"sS") ) - { /* Sign file - Arguments: plaintextfile, your_userid, signedtextfile - Two kinds of signature: full signature certificate, - or just an RSA-signed message digest. - */ - - boolean separate_signature = FALSE; - - separate_signature = strhas( argv[1], 'b' ); - - strcpy( plainfile, argv[2] ); - - if (argc>=5) /* default signature file extension */ - { strcpy( cipherfile, argv[4] ); - default_extension( cipherfile, CTX_EXTENSION ); - } - else - { /* Default the signature file name */ - strcpy( cipherfile, plainfile ); - /* ...but replace file extension: */ - force_extension( cipherfile, CTX_EXTENSION ); - } - - if (strcmp( plainfile, cipherfile ) == 0) - { fprintf(stderr, "\aFile [%s] must be specified just once.\n", plainfile ); - goto user_error; /* same filenames for both files */ - } - - if (argc>=4) - { strcpy( mcguffin, argv[3] ); /* Userid of signer */ - translate_spaces( mcguffin ); /* change all '_' to ' ' */ - } - else - { fprintf(stderr, "\nEnter userid to look up your secret key for signature: "); - getstring( mcguffin, 255, TRUE ); /* echo keyboard */ - } - - if (nestflag) /* user thinks this file has nested info */ - { get_header_info_from_file( plainfile, &ctb, 1); - if (!legal_ctb(ctb)) - { nestflag = FALSE; - fprintf(stderr,"\n\aNo nestable data in plaintext file '%s'.\n",plainfile); - } - } - - status = signfile( nestflag, separate_signature, - mcguffin, plainfile, cipherfile ); - - if (status < 0) /* signfile failed */ - { fprintf(stderr, "\aSignature error\n" ); - goto user_error; - } - - if (uu_emit) - { status = uue_file(cipherfile, SCRATCH_CTX_FILENAME); - remove(cipherfile); /* dangerous. sure hope rename works... */ - rename(SCRATCH_CTX_FILENAME, cipherfile); - } - - if (!verbose) /* if other filename messages were supressed */ - fprintf(stderr,"\nSignature file: %s ", cipherfile); - - exit(0); - } /* Sign file */ - - - /*-------------------------------------------------------*/ - if ( (argc >= 3) && strhasany(argv[1],"eE") ) - { /* Encrypt file - Arguments: plaintextfile, her_userid, ciphertextfile - */ - - strcpy( plainfile, argv[2] ); - - if (argc >= 5) /* default cipher file extension */ - { strcpy( cipherfile, argv[4] ); - default_extension( cipherfile, CTX_EXTENSION ); - } - else - { /* Default the cipherfile name */ - strcpy( cipherfile, plainfile ); - force_extension( cipherfile, CTX_EXTENSION ); - } - - if (strcmp( plainfile, cipherfile) == 0) - { fprintf(stderr, "\aFile [%s] must be specified just once.\n", plainfile ); - goto user_error; /* same filenames for both files */ - } - - if (argc >= 4) - { strcpy( mcguffin, argv[3] ); /* Userid of recipient */ - translate_spaces( mcguffin ); /* change all '_' to ' ' */ - } - else - { fprintf(stderr, "\nEnter userid to look up recipient's public key: "); - getstring( mcguffin, 255, TRUE ); /* echo keyboard */ - } - - if (nestflag) /* user thinks this file has nested info */ - { get_header_info_from_file( plainfile, &ctb, 1); - if (!legal_ctb(ctb)) - { nestflag = FALSE; - fprintf(stderr,"\n\aNo nestable data in plaintext file '%s'.\n",plainfile); - } - } - - if (!nestflag) - { /* Prepend CTB_LITERAL byte to plaintext file. - --sure wish this pass could be optimized away. */ - strcpy( tempfile, plainfile ); - strcpy( plainfile, SCRATCH_PTX_FILENAME ); - status = make_literal( tempfile, plainfile ); - } - status = encryptfile( nestflag, mcguffin, plainfile, cipherfile ); - - if (!nestflag) - { wipefile( SCRATCH_PTX_FILENAME ); - remove( SCRATCH_PTX_FILENAME ); - strcpy( plainfile, tempfile ); - } - - if (status < 0) /* encryptfile failed */ - { fprintf(stderr, "\aEncryption error\n" ); - goto user_error; - } - - if (wipeflag) - { wipefile(plainfile); /* destroy every trace of plaintext */ - remove(plainfile); - fprintf(stderr,"\nFile %s wiped and deleted. ",plainfile); - } - - if (uu_emit) - { status = uue_file(cipherfile, SCRATCH_CTX_FILENAME); - remove(cipherfile); /* dangerous. sure hope rename works... */ - rename(SCRATCH_CTX_FILENAME, cipherfile); - } - - if (!verbose) /* if other filename messages were supressed */ - fprintf(stderr,"\nCiphertext file: %s ", cipherfile); - - exit(0); - } /* Encrypt file */ - - - /*-------------------------------------------------------*/ - if ( (argc >= 3) && strhasany(argv[1],"cC") ) - { /* Encrypt file with BassOmatic only - Arguments: plaintextfile, ciphertextfile - */ - - strcpy( plainfile, argv[2] ); - - if (argc >= 4) /* default cipher file extension */ - { strcpy( cipherfile, argv[3] ); - default_extension( cipherfile, CTX_EXTENSION ); - } - else - { /* Default the cipherfile name */ - strcpy( cipherfile, plainfile ); - force_extension( cipherfile, CTX_EXTENSION ); - } - - if (strcmp( plainfile, cipherfile) == 0) - { fprintf(stderr, "\aFile [%s] must be specified just once.\n", plainfile ); - goto user_error; /* same filenames for both files */ - } - - if (nestflag) /* user thinks this file has nested info */ - { get_header_info_from_file( plainfile, &ctb, 1); - if (!legal_ctb(ctb)) - { nestflag = FALSE; - fprintf(stderr,"\n\aNo nestable data in plaintext file '%s'.\n",plainfile); - } - } - - if (!nestflag) - { /* Prepend CTB_LITERAL byte to plaintext file. - --sure wish this pass could be optimized away. */ - strcpy( tempfile, plainfile ); - strcpy( plainfile, SCRATCH_PTX_FILENAME ); - status = make_literal( tempfile, plainfile ); - } - - status = bass_encryptfile( nestflag, plainfile, cipherfile ); - - if (!nestflag) - { wipefile( SCRATCH_PTX_FILENAME ); - remove( SCRATCH_PTX_FILENAME ); - strcpy( plainfile, tempfile ); - } - - if (status < 0) /* encryptfile failed */ - { fprintf(stderr, "\aEncryption error\n" ); - goto user_error; - } - - if (wipeflag) - { wipefile(plainfile); /* destroy every trace of plaintext */ - remove(plainfile); - fprintf(stderr,"\nFile %s wiped and deleted. ",plainfile); - } - - if (uu_emit) - { status = uue_file(cipherfile, SCRATCH_CTX_FILENAME); - remove(cipherfile); /* dangerous. sure hope rename works... */ - rename(SCRATCH_CTX_FILENAME, cipherfile); - } - - if (!verbose) /* if other filename messages were supressed */ - fprintf(stderr,"\nCiphertext file: %s ", cipherfile); - - exit(0); - } /* Encrypt file with BassOmatic only */ - - - /*-------------------------------------------------------*/ - if (argv[1][1] == 'k') - { /* Key generation - Arguments: keyfile, bitcount, bitcount - */ - char keyfile[64], keybits[6], ebits[6]; - - if (argc > 2) - strcpy( keyfile, argv[2] ); - else - strcpy( keyfile, "" ); - - if (argc > 3) - strncpy( keybits, argv[3], sizeof(keybits)-1 ); - else - strcpy( keybits, "" ); - - if (argc > 4) - strncpy( ebits, argv[4], sizeof(ebits)-1 ); - else - strcpy( ebits, "" ); - - status = dokeygen( keyfile, keybits, ebits ); - - if (status < 0) - { fprintf(stderr, "\aKeygen error. " ); - goto user_error; - } - exit(0); - } /* Key generation */ - - /*-------------------------------------------------------*/ - if ((argc >= 3) && (argv[1][1] == 'a')) - { /* Add key to key ring - Arguments: keyfile, ringfile - */ - if (argc < 4) /* default key ring filename */ - buildfilename( ringfile, PUBLIC_KEYRING_FILENAME ); - else - strncpy( ringfile, argv[3], sizeof(ringfile)-1 ); - strncpy( keyfile, argv[2], sizeof(keyfile)-1 ); - - strlwr( keyfile ); - strlwr( ringfile ); - if (! file_exists( keyfile )) - default_extension( keyfile, PUB_EXTENSION ); - - if (strcontains( keyfile, SEC_EXTENSION )) - force_extension( ringfile, SEC_EXTENSION ); - else - force_extension( ringfile, PUB_EXTENSION ); - - if (! file_exists( keyfile )) - { fprintf(stderr, "\n\aKey file '%s' does not exist.\n", keyfile ); - goto user_error; - } - - status = addto_keyring( keyfile, ringfile ); - - if (status < 0) - { fprintf(stderr, "\aKeyring add error. " ); - goto user_error; - } - exit(0); - } /* Add key to key ring */ - - /*-------------------------------------------------------*/ - if ((argc >= 2) - && strhasany( argv[1], "vr" ) ) - { /* View or remove key ring entries, with userid match - Arguments: userid, ringfile - */ - if (argc < 4) /* default key ring filename */ - buildfilename( ringfile, PUBLIC_KEYRING_FILENAME ); - else - strcpy( ringfile, argv[3] ); - - strcpy( mcguffin, argv[2] ); - if (strcmp( mcguffin, "*" ) == 0) - strcpy( mcguffin, "" ); - - translate_spaces( mcguffin ); /* change all '_' to ' ' */ - - if ((argc < 4) - && (strcontains( argv[2], PUB_EXTENSION ) - || strcontains( argv[2], SEC_EXTENSION ))) - { strcpy( ringfile, argv[2] ); - strcpy( mcguffin, "" ); - } - - strlwr( ringfile ); - if (! file_exists( ringfile )) - default_extension( ringfile, PUB_EXTENSION ); - - if (strhas( argv[1], 'v' )) - if (view_keyring( mcguffin, ringfile ) < 0) - { fprintf(stderr, "\aKeyring view error. " ); - goto user_error; - } - if (strhas( argv[1], 'r' )) - if (remove_from_keyring( NULL, mcguffin, ringfile ) < 0) - { fprintf(stderr, "\aKeyring remove error. " ); - goto user_error; - } - exit(0); - } /* view or remove key ring entries, with userid match */ - /*-------------------------------------------------------*/ - - fprintf(stderr, "\aUnrecognizable parameters. " ); - goto user_error; - } /* -options specified */ - - - /*---------------------------------------------------------*/ - /* no options specified */ - - if (argc >= 2) - { /* Decrypt file - Arguments: ciphertextfile, plaintextfile - */ - strcpy( cipherfile, argv[1] ); - default_extension( cipherfile, CTX_EXTENSION ); - - if (argc >= 3) - { strcpy( plainfile, argv[2] ); - default_extension( plainfile, "." ); - } - else - { /* Default the plaintext file name */ - strcpy( plainfile, argv[1] ); - force_extension( plainfile, "." ); - } - - if (strcmp( plainfile, cipherfile ) == 0) - { fprintf(stderr, "\aFile '%s' cannot be both input and output file.\n", plainfile ); - goto user_error; /* error: same filenames for both files */ - } - - if (! file_exists( cipherfile )) - { fprintf(stderr, "\a\nError: Cipher or signature file '%s' does not exist.\n", - cipherfile); - goto user_error; - } - - get_header_info_from_file( cipherfile, header, 4 ); - if (!is_ctb(header[0]) && is_uufile(cipherfile)) - { - if (verbose) fprintf(stderr,"uudecoding %s...",cipherfile); - status = uud_file(cipherfile, SCRATCH_CTX_FILENAME); - if (status==0) - { if (verbose) fprintf(stderr,"...done.\n"); - remove( cipherfile ); /* dangerous. sure hope rename works... */ - rename( SCRATCH_CTX_FILENAME, cipherfile ); - } - else fprintf(stderr,"\n\aError: uudecode failed for file %s\n",cipherfile); - } - - /*---------------------------------------------------------*/ - do /* while nested parsable info present */ - { - if (get_header_info_from_file( cipherfile, &ctb, 1) < 0) - { fprintf(stderr,"\n\aCan't open ciphertext file '%s'\n",cipherfile); - goto user_error; - } - - if (!is_ctb(ctb)) /* not a real CTB -- complain */ - goto reject; - - /* PKE is Public Key Encryption */ - if (is_ctb_type( ctb, CTB_PKE_TYPE )) - { - fprintf(stderr,"\nFile is encrypted. Secret key is required to read it. "); - - status = decryptfile( cipherfile, plainfile ); - - if (status < 0) /* error return */ - goto user_error; - if (status < 1) /* output file has no nested info? */ - break; /* no nested parsable info. exit loop. */ - - /* Nested parsable info indicated. Process it. */ - wipefile( SCRATCH_CTX_FILENAME ); - remove( SCRATCH_CTX_FILENAME ); - rename( plainfile, SCRATCH_CTX_FILENAME ); - strcpy( cipherfile, SCRATCH_CTX_FILENAME ); - continue; /* skip rest of loop */ - } /* outer CTB is PKE type */ - - - if (is_ctb_type( ctb, CTB_SKE_TYPE )) - { - fprintf(stderr,"\nFile has signature. Public key is required to check signature. "); - - status = check_signaturefile( cipherfile, plainfile ); - - if (status < 0) /* error return */ - goto user_error; - - if (status < 1) /* output file has no nested info? */ - break; /* no nested parsable info. exit loop. */ - - /* Nested parsable info indicated. Process it. */ - /* Destroy signed plaintext, if it's in a scratchfile. */ - wipefile( SCRATCH_CTX_FILENAME ); - remove( SCRATCH_CTX_FILENAME ); - rename( plainfile, SCRATCH_CTX_FILENAME ); - strcpy( cipherfile, SCRATCH_CTX_FILENAME ); - continue; /* skip rest of loop */ - } /* outer CTB is SKE type */ - - - if (ctb == CTB_CKE) - { /* Conventional Key Encrypted ciphertext. */ - fprintf(stderr,"\nFile is conventionally encrypted. Pass phrase required to read it. "); - status = bass_decryptfile( cipherfile, plainfile ); - if (status < 0) /* error return */ - goto user_error; - if (status < 1) /* output file has no nested info? */ - break; /* no nested parsable info. exit loop. */ - /* Nested parsable info indicated. Process it. */ - wipefile( SCRATCH_CTX_FILENAME ); - remove( SCRATCH_CTX_FILENAME ); - rename( plainfile, SCRATCH_CTX_FILENAME ); - strcpy( cipherfile, SCRATCH_CTX_FILENAME ); - continue; /* skip rest of loop */ - } /* CTB is CKE type */ - - - if (is_ctb_type( ctb, CTB_COMPRESSED_TYPE )) - { /* Compressed text. */ - status = decompress_file( cipherfile, plainfile ); - if (status < 0) /* error return */ - goto user_error; - /* Always assume nested information... */ - /* Destroy compressed plaintext, if it's in a scratchfile. */ - wipefile( SCRATCH_CTX_FILENAME ); - remove( SCRATCH_CTX_FILENAME ); - rename( plainfile, SCRATCH_CTX_FILENAME ); - strcpy( cipherfile, SCRATCH_CTX_FILENAME ); - continue; /* skip rest of loop */ - } /* CTB is COMPRESSED type */ - - - if (is_ctb_type( ctb, CTB_LITERAL_TYPE )) - { /* Raw plaintext. Just copy it. No more nesting. */ - /* Strip off CTB_LITERAL prefix byte from file: */ - status = strip_literal( cipherfile, plainfile ); - break; /* no nested parsable info. exit loop. */ - } /* CTB is LITERAL type */ - - - if ((ctb == CTB_CERT_SECKEY) - || (ctb == CTB_CERT_PUBKEY)) - { /* Key ring. View it. */ - fprintf(stderr, "\nFile contains key(s). Contents follow..." ); - - if (view_keyring( NULL, cipherfile ) < 0) - goto user_error; - - /* No output file--what should we do with plainfile? - We know that we have already prevented original - cipher filename from being same as plain filename. - */ - - if (strcmp( cipherfile, SCRATCH_CTX_FILENAME ) == 0) - { /* key was nested in signed or enciphered file */ - remove( plainfile ); - rename( cipherfile, plainfile ); - } - exit(0); /* no nested parsable info. */ - /* strcpy(plainfile,""); /* no further nesting */ - /* break; /* no nested parsable info. exit loop. */ - } /* key ring. view it. */ - -reject: fprintf(stderr,"\a\nError: '%s' is not a cipher, signature, or key file.\n", - cipherfile); - goto user_error; - - } while (TRUE); - - /* No more nested parsable information */ - - /* Destroy any sensitive information in scratchfile: */ - wipefile( SCRATCH_CTX_FILENAME ); - remove( SCRATCH_CTX_FILENAME ); - - if (!verbose) /* if other filename messages were supressed */ - fprintf(stderr,"\nPlaintext filename: %s ", plainfile); - - - /*---------------------------------------------------------*/ - - /* One last thing-- let's attempt to classify some of the more - frequently occurring cases of plaintext output files, as an - aid to the user. - - For example, if output file is a public key, it should have - the right extension on the filename. - - Also, it will likely be common to encrypt PKZIP files, so - they should be renamed with the .zip extension. - */ - get_header_info_from_file( plainfile, header, 4 ); - - if (header[0] == CTB_CERT_PUBKEY) - { /* Special case--may be public key, worth renaming */ - fprintf(stderr, "\nPlaintext file '%s' looks like it contains a public key.", - plainfile ); - maybe_force_extension( plainfile, PUB_EXTENSION ); - } /* Possible public key output file */ - - else - if (pkzipSignature( header )) - { /* Special case--may be a PKZIP file, worth renaming */ - fprintf(stderr, "\nPlaintext file '%s' looks like a PKZIP file.", - plainfile ); - maybe_force_extension( plainfile, ".zip" ); - } /* Possible PKZIP output file */ - - else - if ((header[0] == CTB_PKE) - || (header[0] == CTB_SKE) - || (header[0] == CTB_CKE)) - { /* Special case--may be another ciphertext file, worth renaming */ - fprintf(stderr, "\n\aOutput file '%s' may contain more ciphertext or signature.", - plainfile ); - maybe_force_extension( plainfile, ".ctx" ); - } /* Possible ciphertext output file */ - - exit(0); /* no nested parsable info. */ - - } /* Decrypt file, or check signature, or show key */ - - -user_error: /* comes here if user made a boo-boo. */ - fprintf(stderr,"\nFor more help, consult the PGP User's Guide."); - -usage: - fprintf(stderr,"\nUsage summary:"); - fprintf(stderr,"\nTo encrypt a plaintext file with recipent's public key, type:"); - fprintf(stderr,"\n pgp -e textfile her_userid (produces textfile.ctx)"); - fprintf(stderr,"\nTo sign a plaintext file with your secret key, type:"); - fprintf(stderr,"\n pgp -s textfile your_userid (produces textfile.ctx)"); - fprintf(stderr,"\nTo sign a plaintext file with your secret key, and then encrypt it " - "\n with recipent's public key, producing a .ctx file:"); - fprintf(stderr,"\n pgp -es textfile her_userid your_userid"); - fprintf(stderr,"\nTo encrypt with conventional encryption only: pgp -c textfile"); - fprintf(stderr,"\nTo decrypt or check a signature for a ciphertext (.ctx) file:"); - fprintf(stderr,"\n pgp ciphertextfile [plaintextfile]"); - fprintf(stderr,"\nTo generate your own unique public/secret key pair, type: pgp -k"); - fprintf(stderr,"\nTo add a public or secret key file's contents to your public " - "\n or secret key ring: pgp -a keyfile [keyring]"); - fprintf(stderr,"\nTo remove a key from your public key ring: pgp -r userid [keyring]"); - fprintf(stderr,"\nTo view the contents of your public key ring: pgp -v [userid] [keyring] "); - exit(1); /* error exit */ - -} /* main */ - +/* #define TEMP_VERSION / * if defined, temporary experimental version of PGP */ +/* pgp.c -- main module for PGP. + PGP: Pretty Good(tm) Privacy - public key cryptography for the masses. + + Synopsis: PGP uses public-key encryption to protect E-mail. + Communicate securely with people you've never met, with no secure + channels needed for prior exchange of keys. PGP is well featured and + fast, with sophisticated key management, digital signatures, data + compression, and good ergonomic design. + + The original PGP version 1.0 was written by Philip Zimmermann, of + Phil's Pretty Good(tm) Software. Many parts of later versions of + PGP were developed by an international collaborative effort, + involving a number of contributors, including major efforts by: + Branko Lankester + Hal Finney <74076.1041@compuserve.com> + Peter Gutmann + Other contributors who ported or translated or otherwise helped include: + Jean-loup Gailly in France + Hugh Kennedy in Germany + Lutz Frank in Germany + Cor Bosman in The Netherlands + Felipe Rodriquez Svensson in The Netherlands + Armando Ramos in Spain + Miguel Angel Gallardo Ortiz in Spain + Harry Bush and Maris Gabalins in Latvia + Zygimantas Cepaitis in Lithuania + Alexander Smishlajev + Peter Suchkow and Andrew Chernov in Russia + David Vincenzetti in Italy + ...and others. + + + (c) Copyright 1990-1994 by Philip Zimmermann. All rights reserved. + The author assumes no liability for damages resulting from the use + of this software, even if the damage results from defects in this + software. No warranty is expressed or implied. + + Note that while most PGP source modules bear Philip Zimmermann's + copyright notice, many of them have been revised or entirely written + by contributors who frequently failed to put their names in their + code. Code that has been incorporated into PGP from other authors + was either originally published in the public domain or is used with + permission from the various authors. + + PGP is available for free to the public under certain restrictions. + See the PGP User's Guide (included in the release package) for + important information about licensing, patent restrictions on + certain algorithms, trademarks, copyrights, and export controls. + + + Philip Zimmermann may be reached at: + Boulder Software Engineering + 3021 Eleventh Street + Boulder, Colorado 80304 USA + (303) 541-0140 (voice or FAX) + email: prz@acm.org + + + PGP will run on MSDOS, Sun Unix, VAX/VMS, Ultrix, Atari ST, + Commodore Amiga, and OS/2. Note: Don't try to do anything with + this source code without looking at the PGP User's Guide. + + PGP combines the convenience of the Rivest-Shamir-Adleman (RSA) + public key cryptosystem with the speed of fast conventional + cryptographic algorithms, fast message digest algorithms, data + compression, and sophisticated key management. And PGP performs + the RSA functions faster than most other software implementations. + PGP is RSA public key cryptography for the masses. + + Uses RSA Data Security, Inc. MD5 Message Digest Algorithm + as a hash for signatures. Uses the ZIP algorithm for compression. + Uses the ETH IPES/IDEA algorithm for conventional encryption. + + PGP generally zeroes its used stack and memory areas before exiting. + This avoids leaving sensitive information in RAM where other users + could find it later. The RSA library and keygen routines also + sanitize their own stack areas. This stack sanitizing has not been + checked out under all the error exit conditions, when routines exit + abnormally. Also, we must find a way to clear the C I/O library + file buffers, the disk buffers, and and cache buffers. + + Revisions: + Version 1.0 - 5 Jun 91 + Version 1.4 - 19 Jan 92 + Version 1.5 - 12 Feb 92 + Version 1.6 - 24 Feb 92 + Version 1.7 - 29 Mar 92 + Version 1.8 - 23 May 92 + Version 2.0 - 2 Sep 92 + Version 2.1 - 6 Dec 92 + Version 2.2 - 6 Mar 93 + Version 2.3 - 13 Jun 93 + Version 2.3a- 1 Jul 93 + Version 2.4 - 6 Nov 93 + Version 2.5 - 5 May 94 + Version 2.6 - 22 May 94 + + Modified: 12-Nov-92 HAJK + Add FDL stuff for VAX/VMS local mode. +*/ + + +#include +#ifndef AMIGA +#include +#endif +#include +#include +#include +#include "system.h" +#include "mpilib.h" +#include "random.h" +#include "crypto.h" +#include "fileio.h" +#include "keymgmt.h" +#include "language.h" +#include "pgp.h" +#include "exitpgp.h" +#include "charset.h" +#include "getopt.h" +#include "config.h" +#include "keymaint.h" +#include "keyadd.h" +#include "rsaglue.h" +#ifdef M_XENIX +char *strstr(); +long time(); +#endif + +#ifdef MSDOS +#ifdef __ZTC__ /* Extend stack for Zortech C */ +unsigned _stack_ = 24*1024; +#endif +#ifdef __TURBOC__ +unsigned _stklen = 24*1024; +#endif +#endif +#define STACK_WIPE 4096 + +/* Global filenames and system-wide file extensions... */ +char rel_version[] = _LANG("2.6"); /* release version */ +static char rel_date[] = "23 May 94"; /* release date */ +char PGP_EXTENSION[] = ".pgp"; +char ASC_EXTENSION[] = ".asc"; +char SIG_EXTENSION[] = ".sig"; +char BAK_EXTENSION[] = ".bak"; +static char HLP_EXTENSION[] = ".hlp"; +char CONSOLE_FILENAME[] = "_CONSOLE"; +static char HELP_FILENAME[] = "pgp.hlp"; + +/* These files use the environmental variable PGPPATH as a default path: */ +char globalPubringName[MAX_PATH]; +char globalSecringName[MAX_PATH]; +char globalRandseedName[MAX_PATH]; +char globalCommentString[128]; + +/* Flags which are global across the driver code files */ +boolean verbose = FALSE; /* -l option: display maximum information */ +FILE *pgpout; /* Place for routine messages */ + +static void usage(void); +static void key_usage(void); +static void arg_error(void); +static void initsigs(void); +static int do_keyopt(char); +static int do_decrypt(char *); +static void do_armorfile(char *); + + +/* Various compression signatures: PKZIP, Zoo, GIF, Arj, and HPACK. + Lha(rc) is handled specially in the code; it is missing from the + compressSig structure intentionally. If more formats are added, + put them before lharc to keep the code consistent. +*/ +static char *compressSig[] = { "PK\03\04", "ZOO ", "GIF8", "\352\140", + "HPAK", "\037\213", "\037\235", "\032\013", "\032HP%" + /* lharc is special, must be last */ }; +static char *compressName[] = { "PKZIP", "Zoo", "GIF", "Arj", + "Hpack", "gzip", "compressed", "PAK", "Hyper", + "LHarc" }; +static char *compressExt[] = { ".zip", ".zoo", ".gif", ".arj", + ".hpk", ".z", ".Z", ".pak", ".hyp", + ".lzh" }; + +/* "\032\0??", "ARC", ".arc" */ + +int compressSignature(byte *header) +/* Returns file signature type from a number of popular compression formats + or -1 if no match */ +{ + int i; + + for (i=0; i= 0) + return FALSE; /* probably not compressible */ + return TRUE; /* possibly compressible */ +} /* compressible */ + + +/* Possible error exit codes - not all of these are used. Note that we + don't use the ANSI EXIT_SUCCESS and EXIT_FAILURE. To make things + easier for compilers which don't support enum we use #defines */ + +#define EXIT_OK 0 +#define INVALID_FILE_ERROR 1 +#define FILE_NOT_FOUND_ERROR 2 +#define UNKNOWN_FILE_ERROR 3 +#define NO_BATCH 4 +#define BAD_ARG_ERROR 5 +#define INTERRUPT 6 +#define OUT_OF_MEM 7 + +/* Keyring errors: Base value = 10 */ +#define KEYGEN_ERROR 10 +#define NONEXIST_KEY_ERROR 11 +#define KEYRING_ADD_ERROR 12 +#define KEYRING_EXTRACT_ERROR 13 +#define KEYRING_EDIT_ERROR 14 +#define KEYRING_VIEW_ERROR 15 +#define KEYRING_REMOVE_ERROR 16 +#define KEYRING_CHECK_ERROR 17 +#define KEY_SIGNATURE_ERROR 18 +#define KEYSIG_REMOVE_ERROR 19 + +/* Encode errors: Base value = 20 */ +#define SIGNATURE_ERROR 20 +#define RSA_ENCR_ERROR 21 +#define ENCR_ERROR 22 +#define COMPRESS_ERROR 23 + +/* Decode errors: Base value = 30 */ +#define SIGNATURE_CHECK_ERROR 30 +#define RSA_DECR_ERROR 31 +#define DECR_ERROR 32 +#define DECOMPRESS_ERROR 33 + + +#ifdef SIGINT +void breakHandler(int sig) +/* This function is called if a BREAK signal is sent to the program. In this + case we zap the temporary files. +*/ +{ +#ifdef UNIX + if (sig == SIGPIPE) { + signal(SIGPIPE, SIG_IGN); + exitPGP(INTERRUPT); + } + if (sig != SIGINT) + fprintf(stderr, "\nreceived signal %d\n", sig); + else +#endif + fprintf(pgpout,LANG("\nStopped at user request\n")); + exitPGP(INTERRUPT); +} +#endif + + +static void clearscreen(void) +{ /* Clears screen and homes the cursor. */ + fprintf(pgpout,"\n\033[0;0H\033[J\r \r"); /* ANSI sequence. */ + fflush(pgpout); +} + +static void signon_msg(void) +{ /* We had to process the config file first to possibly select the + foreign language to translate the sign-on line that follows... */ + word32 tstamp; + /* display message only once to allow calling multiple times */ + static boolean printed = FALSE; + + if (quietmode || printed) + return; + printed = TRUE; + fprintf(stderr,LANG("Pretty Good Privacy(tm) %s - Public-key encryption for the masses.\n"), + rel_version); +#ifdef TEMP_VERSION + fputs("Internal development version only - not for general release.\n", stderr); +#endif + fprintf(stderr, LANG("(c) 1990-1994 Philip Zimmermann, Phil's Pretty Good Software. %s\n"), + rel_date); + fputs(LANG (signon_legalese), stderr); + fputs(LANG("Export of this software may be restricted by the U.S. government.\n"),stderr); + + get_timestamp((byte *)&tstamp); /* timestamp points to tstamp */ + fprintf(pgpout,"Current time: %s\n",ctdate(&tstamp)); +} + + +#ifdef TEMP_VERSION /* temporary experimental version of PGP */ +#include +#define CREATION_DATE 0x2DC079A4ul + /* CREATION_DATE is Fri Apr 29 03:06:12 1994 UTC */ +#define LIFESPAN ((unsigned long) 60L * (unsigned long) 86400L) + /* LIFESPAN is 60 days */ +void check_expiration_date(void) +/* If this is an experimental version of PGP, cut its life short */ +{ + if (get_timestamp(NULL) > (CREATION_DATE + LIFESPAN)) { + fprintf(stderr,"\n\007This experimental version of PGP has expired.\n"); + exit(-1); /* error exit */ + } +} /* check_expiration_date */ +#else /* no expiration date */ +#define check_expiration_date() /* null statement */ +#endif /* TEMP_VERSION */ + + + +/* -f means act as a unix-style filter */ +/* -i means internalize extended file attribute information, only supported + * between like (or very compatible) operating systems. */ +/* -l means show longer more descriptive diagnostic messages */ +/* -m means display plaintext output on screen, like unix "more" */ +/* -d means decrypt only, leaving inner signature wrapping intact */ +/* -t means treat as pure text and convert to canonical text format */ + +/* Used by getopt function... */ +#define OPTIONS "abcdefghiklmo:prstu:vwxz:ABCDEFGHIKLMO:PRSTU:VWX?" +extern int optind; +extern char *optarg; + +boolean emit_radix_64 = FALSE; /* set by config file */ +static boolean sign_flag = FALSE; +boolean moreflag = FALSE; +boolean filter_mode = FALSE; +static boolean preserve_filename = FALSE; +static boolean decrypt_only_flag = FALSE; +static boolean de_armor_only = FALSE; +static boolean strip_sig_flag = FALSE; +boolean clear_signatures = TRUE; +boolean strip_spaces; +static boolean c_flag = FALSE; +boolean encrypt_to_self = FALSE; /* should I encrypt messages to myself? */ +boolean batchmode = FALSE; /* if TRUE: don't ask questions */ +boolean quietmode = FALSE; +boolean force_flag = FALSE; /* overwrite existing file without asking */ +boolean pkcs_compat = 1; +#ifdef VMS /* kludge for those stupid VMS variable-length text records */ +char literal_mode = MODE_TEXT; /* MODE_TEXT or MODE_BINARY for literal packet */ +#else /* not VMS */ +char literal_mode = MODE_BINARY; /* MODE_TEXT or MODE_BINARY for literal packet */ +#endif /* not VMS */ +/* my_name is substring of default userid for secret key to make signatures */ +char my_name[256] = "\0"; /* null my_name means take first userid in ring */ +boolean keepctx = FALSE; /* TRUE means keep .ctx file on decrypt */ +/* Ask for each key separately if it should be added to the keyring */ +boolean interactive_add = FALSE; +boolean compress_enabled = TRUE; /* attempt compression before encryption */ +long timeshift = 0L; /* seconds from GMT timezone */ +boolean legal_kludge; +int version_byte = VERSION_BYTE_OLD; +boolean nomanual = 0; + + +static boolean attempt_compression; /* attempt compression before encryption */ +static char *outputfile = NULL; +static int errorLvl = EXIT_OK; +static char mcguffin[256]; /* userid search tag */ +boolean signature_checked = FALSE; +char plainfile[MAX_PATH]; +int myArgc = 2; +char **myArgv; +struct hashedpw *passwds = 0, *keypasswds = 0; +static struct hashedpw **passwdstail = &passwds; + +int main(int argc, char *argv[]) +{ + int status, opt; + char *inputfile = NULL; + char **recipient = NULL; + char **mcguffins; + char *workfile, *tempf; + boolean nestflag = FALSE; + boolean decrypt_mode = FALSE; + boolean wipeflag = FALSE; + boolean armor_flag = FALSE; /* -a option */ + boolean separate_signature = FALSE; + boolean keyflag = FALSE; + boolean encrypt_flag = FALSE; + boolean conventional_flag = FALSE; + char *clearfile = NULL; + char *literal_file = NULL; + char literal_file_name[MAX_PATH]; + char cipherfile[MAX_PATH]; + char keychar = '\0'; + char *p; + byte ctb; + struct hashedpw *hpw; + + /* Initial messages to stderr */ + pgpout = stderr; + +#ifdef DEBUG1 + verbose = TRUE; +#endif + /* The various places one can get passwords from. + * We accumulate them all into two lists. One is + * to try on keys only, and is stored in no particular + * order, while the other is of unknown purpose so + * far (they may be used for conventional encryption + * or decryption as well), and are kept in a specific + * order. If any password in the general list is found + * to decode a key, it is moved to the key list. + * The general list is not grown after initialization, + * so the tail pointer is not used after this. + */ + + if ((p = getenv("PGPPASS")) != NULL) { + hpw = xmalloc(sizeof(struct hashedpw)); + hashpass(p, strlen(p), hpw->hash); + /* Add to linked list of key passwords */ + hpw->next = keypasswds; + keypasswds = hpw; + } + + /* The -z "password" option should be used instead of PGPPASS if + * the environment can be displayed with the ps command (eg. BSD). + * If the system does not allow overwriting of the command line + * argument list but if it has a "hidden" environment, PGPPASS + * should be used. + */ + for (opt = 1; opt < argc; ++opt) { + p = argv[opt]; + if (p[0] != '-' || p[1] != 'z') + continue; + /* Accept either "-zpassword" or "-z password" */ + p += 2; + if (!*p) + p = argv[++opt]; + /* p now points to password */ + if (!p) + break; /* End of arg list - ignore */ + hpw = xmalloc(sizeof(struct hashedpw)); + hashpass(p, strlen(p), hpw->hash); + /* Wipe password */ + while (*p) + *p++ = ' '; + /* Add to tail of linked list of passwords */ + hpw->next = 0; + *passwdstail = hpw; + passwdstail = &hpw->next; + } + /* + * If PGPPASSFD is set in the environment try to read the password + * from this file descriptor. If you set PGPPASSFD to 0 pgp will + * use the first line read from stdin as password. + */ + if ((p = getenv("PGPPASSFD")) != NULL) + { + int passfd; + if (*p && (passfd = atoi(p)) >= 0) + { + char pwbuf[256]; + p = pwbuf; + while (read(passfd, p, 1) == 1 && *p != '\n') + ++p; + hpw = xmalloc(sizeof(struct hashedpw)); + hashpass(pwbuf, p-pwbuf, hpw->hash); + memset(pwbuf, 0, p-pwbuf); + /* Add to tail of linked list of passwords */ + hpw->next = 0; + *passwdstail = hpw; + passwdstail = &hpw->next; + } + } + + /* Process the config file. The following override each other: + - Hard-coded defualts + - The system config file + - Hard-coded defaults for security-critical things + - The user's config file + - Environment variables + - Command-line options. + */ + opt = 0; /* Number of config files read */ +#ifdef PGP_SYSTEM_DIR + strcpy(mcguffin, PGP_SYSTEM_DIR); + strcat(mcguffin, "config.txt"); + if (access(mcguffin, 0) == 0) { + opt++; + /* + * Note: errors here are NOT fatal, so that people + * can use PGP with a corrputed system file. + */ + processConfigFile(mcguffin); + } +#endif + + /* + * These must be personal; the system config file may not + * influence them. + */ + buildfilename(globalPubringName, "pubring.pgp"); + buildfilename(globalSecringName, "secring.pgp"); + buildfilename(globalRandseedName, "randseed.bin"); + my_name[0] = '\0'; + + /* Process the config file first. Any command-line arguments will + override the config file settings */ + buildfilename( mcguffin, "config.txt" ); + if (access(mcguffin, 0) == 0) { + opt++; + if ( processConfigFile( mcguffin ) < 0 ) + exit(BAD_ARG_ERROR); + } + if (!opt) + fprintf(pgpout, LANG("\007No configuration file found.\n")); + + init_charset(); + +#ifdef MSDOS /* only on MSDOS systems */ + if ((p = getenv("TZ")) == NULL || *p == '\0') { + fprintf(pgpout,LANG("\007WARNING: Environmental variable TZ is not defined, so GMT timestamps\n\ +may be wrong. See the PGP User's Guide to properly define TZ\n\ +in AUTOEXEC.BAT file.\n")); + } +#endif /* MSDOS */ + +#ifdef VMS +#define TEMP "SYS$SCRATCH" +#else +#define TEMP "TMP" +#endif /* VMS */ + if ((p = getenv(TEMP)) != NULL && *p != '\0') + settmpdir(p); + + /* Turn on incompatibility as of 1 September 1994 (GMT) */ + legal_kludge = (get_timestamp(NULL) >= 0x2e651980); + + if ((myArgv = (char **) malloc((argc + 2) * sizeof(char **))) == NULL) { + fprintf(stderr, LANG("\n\007Out of memory.\n")); + exitPGP(7); + } + myArgv[0] = NULL; + myArgv[1] = NULL; + + /* Process all the command-line option switches: */ + while (optind < argc) { + /* + * Allow random order of options and arguments (like GNU getopt) + * NOTE: this does not work with GNU getopt, use getopt.c from + * the PGP distribution. + */ + if ((opt = pgp_getopt(argc, argv, OPTIONS)) == EOF) { + if (optind == argc) /* -- at end */ + break; + myArgv[myArgc++] = argv[optind++]; + continue; + } + opt = to_lower(opt); + if (keyflag && (keychar == '\0' || (keychar == 'v' && opt == 'v'))) + { + if (keychar == 'v') + keychar = 'V'; + else + keychar = opt; + continue; + } + switch (opt) { + case 'a': armor_flag = TRUE; emit_radix_64 = 1; break; + case 'b': separate_signature = strip_sig_flag = TRUE; break; + case 'c': encrypt_flag = conventional_flag = TRUE; + c_flag = TRUE; break; + case 'd': decrypt_only_flag = TRUE; break; + case 'e': encrypt_flag = TRUE; break; + case 'f': filter_mode = TRUE; break; + case '?': + case 'h': usage(); break; +#ifdef VMS + case 'i': literal_mode = MODE_LOCAL; break; +#endif /* VMS */ + case 'k': keyflag = TRUE; break; + case 'l': verbose = TRUE; break; + case 'm': moreflag = TRUE; break; + case 'p': preserve_filename = TRUE; break; + case 'o': outputfile = optarg; break; + case 's': sign_flag = TRUE; break; + case 't': literal_mode = MODE_TEXT; break; + case 'u': strncpy(my_name, optarg, sizeof(my_name)-1); + CONVERT_TO_CANONICAL_CHARSET(my_name); + break; + case 'w': wipeflag = TRUE; break; + case 'z': break; + /* '+' special option: does not require - */ + case '+': + if (processConfigLine(optarg) == 0) + break; + fprintf(stderr, "\n"); + /* fallthrough */ + default: + arg_error(); + } + } + myArgv[myArgc] = NULL; /* Just to make it NULL terminated */ + + if (keyflag && keychar == '\0') + key_usage(); + + signon_msg(); + check_expiration_date(); /* hobble any experimental version */ + + if (legal_kludge) + version_byte = VERSION_BYTE_KLUDGE; + +#if 1 + /* At request of Peter Simons, use stderr always. Sounds reasonable. */ + /* JIS: Put this code back in... removing it broke too many things */ + if (!filter_mode && (outputfile == NULL || strcmp(outputfile, "-"))) + pgpout = stdout; +#endif + +#if 0 + /* Check for the existence of the manual */ + /* Commented out to make PGP less facist */ + if (manuals_missing()) { + fputs(LANG("\nWARNING: PGP User's Guide not found. PGP should not be\n\ +distributed without the User's Guide.\n"), pgpout); + } +#endif + + +#if defined(UNIX) || defined(VMS) + umask(077); /* Make files default to private */ +#endif + + initsigs(); /* Catch signals */ + noise(); /* Start random number generation */ + + if (keyflag) { + status = do_keyopt(keychar); + if (status < 0) + user_error(); + exitPGP(status); + } + + /* -db means break off signature certificate into separate file */ + if (decrypt_only_flag && strip_sig_flag) + decrypt_only_flag = FALSE; + + if (decrypt_only_flag && armor_flag) + decrypt_mode = de_armor_only = TRUE; + + if (outputfile != NULL) + preserve_filename = FALSE; + + if (!sign_flag && !encrypt_flag && !conventional_flag && !armor_flag) + { + if (wipeflag) { /* wipe only */ + if (myArgc != 3) + arg_error(); /* need one argument */ + if (wipefile(myArgv[2]) == 0 && remove(myArgv[2]) == 0) + { + fprintf(pgpout,LANG("\nFile %s wiped and deleted. "),myArgv[2]); + fprintf(pgpout, "\n"); + exitPGP(EXIT_OK); + } + exitPGP(UNKNOWN_FILE_ERROR); + } + /* decrypt if none of the -s -e -c -a -w options are specified */ + decrypt_mode = TRUE; + } + + if (myArgc == 2) /* no arguments */ + { +#ifdef UNIX + if (!filter_mode && !isatty(fileno(stdin))) { + /* piping to pgp without arguments and no -f: + * switch to filter mode but don't write output to stdout + * if it's a tty, use the preserved filename */ + if (!moreflag) + pgpout = stderr; + filter_mode = TRUE; + if (isatty(fileno(stdout)) && !moreflag) + preserve_filename = TRUE; + } +#endif + if (!filter_mode) { + if (quietmode) { + quietmode = FALSE; + signon_msg(); + } + fprintf(pgpout,LANG("\nFor details on licensing and distribution, see the PGP User's Guide.\ +\nFor other cryptography products and custom development services, contact:\ +\nPhilip Zimmermann, 3021 11th St, Boulder CO 80304 USA, phone +1 303 541-0140\n")); + if (strcmp((p = LANG("@translator@")), "@translator@")) + fprintf(pgpout, p); + fprintf(pgpout,LANG("\nFor a usage summary, type: pgp -h\n")); + exit(BAD_ARG_ERROR); /* error exit */ + } + } else { + if (filter_mode) { + recipient = &myArgv[2]; + } else { + inputfile = myArgv[2]; + recipient = &myArgv[3]; + } + } + + + if (filter_mode) { + inputfile = "stdin"; + } else { + if (decrypt_mode && no_extension(inputfile)) { + strcpy(cipherfile, inputfile); + force_extension( cipherfile, ASC_EXTENSION ); + if (file_exists (cipherfile)) { + inputfile = cipherfile; + } else { + force_extension( cipherfile, PGP_EXTENSION ); + if (file_exists (cipherfile)) { + inputfile = cipherfile; + } else { + force_extension( cipherfile, SIG_EXTENSION ); + if (file_exists (cipherfile)) + inputfile = cipherfile; + } + } + } + if (! file_exists( inputfile )) { + fprintf(pgpout, LANG("\007File [%s] does not exist.\n"), inputfile); + errorLvl = FILE_NOT_FOUND_ERROR; + user_error(); + } + } + + if (strlen(inputfile) >= (unsigned) MAX_PATH-4) { + fprintf(pgpout, LANG("\007Invalid filename: [%s] too long\n"), inputfile ); + errorLvl = INVALID_FILE_ERROR; + user_error(); + } + strcpy(plainfile, inputfile); + + if (filter_mode) { + setoutdir(NULL); /* NULL means use tmpdir */ + } else { + if (outputfile) + setoutdir(outputfile); + else + setoutdir(inputfile); + } + + if (filter_mode) { + workfile = tempfile(TMP_WIPE|TMP_TMPDIR); + readPhantomInput(workfile); + } else { + workfile = inputfile; + } + + get_header_info_from_file( workfile, &ctb, 1 ); + if (decrypt_mode) { + strip_spaces = FALSE; + if (!is_ctb(ctb) && is_armor_file(workfile, 0L)) + do_armorfile(workfile); + else + if (do_decrypt(workfile) < 0) + user_error(); + if (batchmode && !signature_checked) + exitPGP(1); /* alternate success, file did not have sig. */ + else + exitPGP(EXIT_OK); + } + + /* + * See if plaintext input file was actually created by PGP earlier-- + * If it was, maybe we should NOT encapsulate it in a literal packet. + * Otherwise, always encapsulate it. + */ + if (force_flag) /* for use with batchmode, force nesting */ + nestflag = legal_ctb(ctb); + else + nestflag = FALSE; /* First assume we will encapsulate it. */ + + if (!batchmode && !filter_mode && legal_ctb(ctb)) { + /* Special case--may be a PGP-created packet, so + do we inhibit encapsulation in literal packet? */ + fprintf(pgpout, LANG("\n\007Input file '%s' looks like it may have been created by PGP. "), + inputfile ); + fprintf(pgpout, LANG("\nIs it safe to assume that it was created by PGP (y/N)? ")); + nestflag = getyesno('n'); + } /* Possible ciphertext input file */ + + if (moreflag) { /* special name to cause printout on decrypt */ + strcpy (literal_file_name, CONSOLE_FILENAME); + literal_mode = MODE_TEXT; /* will check for text file later */ + } else { + strcpy (literal_file_name, inputfile); +#ifdef MSDOS + strlwr (literal_file_name); +#endif + } + literal_file = literal_file_name; + + /* Make sure non-text files are not accidentally converted + to canonical text. This precaution should only be followed + for US ASCII text files, since European text files may have + 8-bit character codes and still be legitimate text files + suitable for conversion to canonical (CR/LF-terminated) + text format. */ + if (literal_mode==MODE_TEXT && !is_text_file(workfile)) { + fprintf(pgpout, +LANG("\nNote: '%s' is not a pure text file.\nFile will be treated as binary data.\n"), + workfile); + literal_mode = MODE_BINARY; /* now expect straight binary */ + } + + if (moreflag && literal_mode==MODE_BINARY) { /* For eyes only? Can't display binary file. */ + fprintf(pgpout, + LANG("\n\007Error: Only text files may be sent as display-only.\n")); + errorLvl = INVALID_FILE_ERROR; + user_error(); + } + + + /* See if plainfile looks like it might be incompressible, + by examining its contents for compression headers for + commonly-used compressed file formats like PKZIP, etc. + Remember this information for later, when we are deciding + whether to attempt compression before encryption. + */ + attempt_compression = compress_enabled && file_compressible(plainfile); + + + if (sign_flag) { + if (!filter_mode && !quietmode) + fprintf(pgpout, LANG("\nA secret key is required to make a signature. ")); + if (!quietmode && my_name[0] == '\0') { + fprintf(pgpout, LANG("\nYou specified no user ID to select your secret key,\n\ +so the default user ID and key will be the most recently\n\ +added key on your secret keyring.\n")); + } + + strip_spaces = FALSE; + clearfile = NULL; + if (literal_mode==MODE_TEXT) { + /* Text mode requires becoming canonical */ + tempf = tempfile(TMP_WIPE|TMP_TMPDIR); + /* +clear means output file with signature in the clear, + only in combination with -t and -a, not with -e or -b */ + if (!encrypt_flag && !separate_signature && + emit_radix_64 && clear_signatures) { + clearfile = workfile; + strip_spaces = TRUE; + } + make_canonical( workfile, tempf ); + if (!clearfile) + rmtemp(workfile); + workfile = tempf; + } + if ((emit_radix_64 || encrypt_flag) && !separate_signature) + tempf = tempfile(TMP_WIPE|TMP_TMPDIR); + else + tempf = tempfile(TMP_WIPE); + /* for clear signatures we create a separate signature */ + status = signfile(nestflag, separate_signature || (clearfile != NULL), + my_name, workfile, tempf, literal_mode, literal_file ); + rmtemp(workfile); + workfile = tempf; + + if (status < 0) { /* signfile failed */ + fprintf(pgpout, LANG("\007Signature error\n") ); + errorLvl = SIGNATURE_ERROR; + user_error(); + } + /* + * We used to compress signed files only if they were also + * armored. Now that we have clear signatures it makes more + * sense to always compress signature files. + */ + if (attempt_compression && !separate_signature && !encrypt_flag + && !clearfile) + { + tempf = tempfile(TMP_WIPE|TMP_TMPDIR); + squish_file(workfile, tempf); + rmtemp(workfile); + workfile = tempf; + } + + } else if (!nestflag) { /* !sign_file */ + /* Prepend CTB_LITERAL byte to plaintext file. + --sure wish this pass could be optimized away. */ + tempf = tempfile(TMP_WIPE); + status = make_literal( workfile, tempf, literal_mode, literal_file ); + rmtemp(workfile); + workfile = tempf; + } + + if (encrypt_flag) { + tempf = tempfile(TMP_WIPE); + if (!conventional_flag) { + if (!filter_mode && !quietmode) + fprintf(pgpout, LANG("\n\nRecipients' public key(s) will be used to encrypt. ")); + if (recipient == NULL || *recipient == NULL || **recipient == '\0') + { + /* no recipient specified on command line */ + fprintf(pgpout, LANG("\nA user ID is required to select the recipient's public key. ")); + fprintf(pgpout, LANG("\nEnter the recipient's user ID: ")); + getstring( mcguffin, 255, TRUE ); /* echo keyboard */ + if ((mcguffins = (char **) malloc (2 * sizeof(char *))) == NULL) { + fprintf(stderr, LANG("\n\007Out of memory.\n")); + exitPGP(7); + } + mcguffins[0] = mcguffin; + mcguffins[1] = ""; + } else { + /* recipient specified on command line */ + mcguffins = recipient; + } + for (recipient = mcguffins; *recipient != NULL && + **recipient != '\0'; recipient++) { + CONVERT_TO_CANONICAL_CHARSET(*recipient); + } + status = encryptfile( mcguffins, workfile, tempf, attempt_compression); + } else { + status = idea_encryptfile( workfile, tempf, attempt_compression); + } + + rmtemp(workfile); + workfile = tempf; + + if (status < 0) { + fprintf(pgpout, LANG("\007Encryption error\n") ); + errorLvl = (conventional_flag ? ENCR_ERROR : RSA_ENCR_ERROR); + user_error(); + } + } /* encrypt file */ + + if (outputfile) /* explicit output file overrides filter mode */ + filter_mode = (strcmp(outputfile, "-") == 0); + + if (filter_mode) { + if (emit_radix_64) { + /* NULL for outputfile means write to stdout */ + if (armor_file(workfile, NULL, inputfile, clearfile) != 0) + { + errorLvl = UNKNOWN_FILE_ERROR; + user_error(); + } + if (clearfile) + rmtemp(clearfile); + } else { + if (writePhantomOutput(workfile) < 0) { + errorLvl = UNKNOWN_FILE_ERROR; + user_error(); + } + } + rmtemp(workfile); + } else { + char name[MAX_PATH]; + if (outputfile) { + strcpy(name, outputfile); + } else { + strcpy(name, inputfile); + drop_extension(name); + } + if (no_extension(name)) { + if (emit_radix_64) + force_extension(name, ASC_EXTENSION); + else if (sign_flag && separate_signature) + force_extension(name, SIG_EXTENSION); + else + force_extension(name, PGP_EXTENSION); + } + if (emit_radix_64) { + if (armor_file(workfile, name, inputfile, clearfile) != 0) + { + errorLvl = UNKNOWN_FILE_ERROR; + user_error(); + } + if (clearfile) + rmtemp(clearfile); + } else { + if ((outputfile = savetemp(workfile, name)) == NULL) { + errorLvl = UNKNOWN_FILE_ERROR; + user_error(); + } + if (!quietmode) { + if (encrypt_flag) + fprintf(pgpout, LANG("\nCiphertext file: %s\n"), outputfile); + else if (sign_flag) + fprintf(pgpout, LANG("\nSignature file: %s\n"), outputfile); + } + } + } + + if (wipeflag) { + /* destroy every trace of plaintext */ + if (wipefile(inputfile) == 0) { + remove(inputfile); + fprintf(pgpout,LANG("\nFile %s wiped and deleted. "),inputfile); + fprintf(pgpout, "\n"); + } + } + + exitPGP(EXIT_OK); + return 0; /* to shut up lint and some compilers */ +} /* main */ + +#ifdef MSDOS +#include +static char *dos_errlst[] = { + "Write protect error", /* LANG ("Write protect error") */ + "Unknown unit", + "Drive not ready", /* LANG ("Drive not ready") */ + "3", "4", "5", "6", "7", "8", "9", + "Write error", /* LANG ("Write error") */ + "Read error", /* LANG ("Read error") */ + "General failure", +}; + +/* handler for msdos 'harderrors' */ +#ifndef OS2 +#ifdef __TURBOC__ /* Turbo C 2.0 */ +static int dostrap(int errval) +#else +static void dostrap(unsigned deverr, unsigned errval) +#endif +{ + char errbuf[64]; + int i; + sprintf(errbuf, "\r\nDOS error: %s\r\n", dos_errlst[errval]); + i = 0; + do + bdos(2,(unsigned int)errbuf[i],0); + while (errbuf[++i]); +#ifdef __TURBOC__ + return 0; /* ignore (fopen will return NULL) */ +#else + return; +#endif +} +#endif /* MSDOS */ +#endif + +static void initsigs() +{ +#ifdef MSDOS +#ifndef OS2 +#ifdef __TURBOC__ + harderr(dostrap); +#else /* MSC */ +#ifndef __GNUC__ /* DJGPP's not MSC */ + _harderr(dostrap); +#endif +#endif +#endif +#endif /* MSDOS */ +#ifdef SIGINT +#ifdef ATARI + signal(SIGINT,(sigfunc_t) breakHandler); +#else + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT,breakHandler); +#if defined(UNIX) || defined(VMS) + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP,breakHandler); + if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) + signal(SIGQUIT,breakHandler); +#ifdef UNIX + signal(SIGPIPE,breakHandler); +#endif + signal(SIGTERM,breakHandler); +#ifndef DEBUG + signal(SIGTRAP,breakHandler); + signal(SIGSEGV,breakHandler); + signal(SIGILL,breakHandler); +#ifdef SIGBUS + signal(SIGBUS,breakHandler); +#endif +#endif /* DEBUG */ +#endif /* UNIX */ +#endif /* not Atari */ +#endif /* SIGINT */ +} /* initsigs */ + + +static void do_armorfile(char *armorfile) +{ + char *tempf; + char cipherfile[MAX_PATH]; + long linepos = 0; + int status; + int success = 0; + + for (;;) { + /* Handle transport armor stripping */ + tempf = tempfile(0); + strip_spaces = FALSE; /* de_armor_file() sets this for clear signature files */ + status = de_armor_file(armorfile,tempf,&linepos); + if (status) { + fprintf(pgpout,LANG("\n\007Error: Transport armor stripping failed for file %s\n"),armorfile); + errorLvl = INVALID_FILE_ERROR; + user_error(); /* Bad file */ + } + if (keepctx || de_armor_only) { + if (outputfile && de_armor_only) { + if (strcmp(outputfile, "-") == 0) { + writePhantomOutput(tempf); + rmtemp(tempf); + return; + } + strcpy(cipherfile, outputfile); + } else { + strcpy(cipherfile, file_tail(armorfile)); + force_extension(cipherfile, PGP_EXTENSION); + } + if ((tempf = savetemp(tempf, cipherfile)) == NULL) { + errorLvl = UNKNOWN_FILE_ERROR; + user_error(); + } + if (!quietmode) + fprintf(pgpout,LANG("Stripped transport armor from '%s', producing '%s'.\n"), + armorfile, tempf); + /* -da flag: don't decrypt */ + if (de_armor_only || do_decrypt(tempf) >= 0) + ++success; + } else { + if (do_decrypt(tempf) >= 0) + ++success; + rmtemp(tempf); + } + + if (!is_armor_file(armorfile, linepos)) { + if (!success) /* print error msg if we didn't decrypt anything */ + user_error(); + return; + } + + fprintf (pgpout, LANG("\nLooking for next packet in '%s'...\n"), armorfile); + } +} /* do_armorfile */ + + +static int do_decrypt(char *cipherfile) +{ + char *outfile = NULL; + int status, i; + boolean nested_info = FALSE; + char ringfile[MAX_PATH]; + byte ctb; + byte header[8]; /* used to classify file type at the end. */ + char preserved_name[MAX_PATH]; + char *newname; + + /* will be set to the original file name after processing a literal packet */ + preserved_name[0] = '\0'; + + do { /* while nested parsable info present */ + if (nested_info) { + rmtemp(cipherfile); /* never executed on first pass */ + cipherfile = outfile; + } + if (get_header_info_from_file( cipherfile, &ctb, 1) < 0) + { + fprintf(pgpout,LANG("\n\007Can't open ciphertext file '%s'\n"),cipherfile); + errorLvl = FILE_NOT_FOUND_ERROR; + return -1; + } + + if (!is_ctb(ctb)) /* not a real CTB -- complain */ + break; + + if (moreflag) + outfile = tempfile(TMP_WIPE|TMP_TMPDIR); + else + outfile = tempfile(TMP_WIPE); + + /* PKE is Public Key Encryption */ + if (is_ctb_type( ctb, CTB_PKE_TYPE )) { + + if (!quietmode) + fprintf(pgpout,LANG("\nFile is encrypted. Secret key is required to read it. ")); + + /* Decrypt to scratch file since we may have a LITERAL2 */ + status = decryptfile( cipherfile, outfile ); + + if (status < 0) { /* error return */ + errorLvl = RSA_DECR_ERROR; + return -1; + } + nested_info = (status > 0); + + } else if (is_ctb_type( ctb, CTB_SKE_TYPE )) { + + if (decrypt_only_flag) { + /* swap file names instead of just copying the file */ + rmtemp(outfile); + outfile = cipherfile; + cipherfile = NULL; + if (!quietmode) + fprintf(pgpout,LANG("\nThis file has a signature, which will be left in place.\n")); + break; /* Do no more */ + } + if (!quietmode) + fprintf(pgpout,LANG("\nFile has signature. Public key is required to check signature. ")); + + status = check_signaturefile( cipherfile, outfile, strip_sig_flag, preserved_name ); + + if (status < 0) { /* error return */ + errorLvl = SIGNATURE_CHECK_ERROR; + return -1; + } + nested_info = (status > 0); + + if (strcmp(preserved_name, "/dev/null") == 0) { + rmtemp(outfile); + fprintf(pgpout, "\n"); + return 0; + } + + } else if (is_ctb_type( ctb, CTB_CKE_TYPE )) { + + /* Conventional Key Encrypted ciphertext. */ + /* Tell user it's encrypted here, and prompt for password in subroutine. */ + if (!quietmode) + fprintf(pgpout,LANG("\nFile is conventionally encrypted. ")); + /* Decrypt to scratch file since it may be a LITERAL2 */ + status = idea_decryptfile( cipherfile, outfile ); + if (status < 0) /* error return */ + { errorLvl = DECR_ERROR; + return -1; /* error exit status */ + } + nested_info = (status > 0); + + } else if (is_ctb_type( ctb, CTB_COMPRESSED_TYPE )) { + + /* Compressed text. */ + status = decompress_file( cipherfile, outfile ); + if (status < 0) { /* error return */ + errorLvl = DECOMPRESS_ERROR; + return -1; + } + /* Always assume nested information... */ + nested_info = TRUE; + + } else if (is_ctb_type( ctb, CTB_LITERAL_TYPE ) || + is_ctb_type( ctb, CTB_LITERAL2_TYPE)) + { /* Raw plaintext. Just copy it. No more nesting. */ + + /* Strip off CTB_LITERAL prefix byte from file: */ + /* strip_literal may alter plainfile; will set mode */ + status = strip_literal( cipherfile, outfile, + preserved_name, &literal_mode); + if (status < 0) { /* error return */ + errorLvl = UNKNOWN_FILE_ERROR; + return -1; + } + nested_info = FALSE; + } else if (ctb==CTB_CERT_SECKEY || ctb==CTB_CERT_PUBKEY) { + + rmtemp(outfile); + if (decrypt_only_flag) { + /* swap file names instead of just copying the file */ + outfile = cipherfile; + cipherfile = NULL; + break; /* no further processing */ + } + /* Key ring. View it. */ + fprintf(pgpout, LANG("\nFile contains key(s). Contents follow...") ); + if (view_keyring( NULL, cipherfile, TRUE, FALSE ) < 0) + { + errorLvl = KEYRING_VIEW_ERROR; + return -1; + } + /* filter mode explicit requested with -f */ + if (filter_mode && !preserve_filename) + return 0; /* No output file */ + if (batchmode) + return 0; + if (ctb == CTB_CERT_SECKEY) + strcpy(ringfile, globalSecringName); + else + strcpy(ringfile, globalPubringName); + /* Ask if it should be put on key ring */ + fprintf(pgpout, LANG("\nDo you want to add this keyfile to keyring '%s' (y/N)? "), ringfile); + if (!getyesno('n')) + return 0; + status = addto_keyring(cipherfile,ringfile); + if (status < 0) { + fprintf(pgpout, LANG("\007Keyring add error. ") ); + errorLvl = KEYRING_ADD_ERROR; + return -1; + } + return 0; /* No output file */ + + } else { /* Unrecognized CTB */ + break; + } + + } while (nested_info); + /* No more nested parsable information */ + + /* Stopped early due to error */ + if (nested_info) { + fprintf(pgpout, "\7\nERROR: Nested data has unexpected format. CTB=0x%02X\n", ctb); + if (outfile) + rmtemp(outfile); + if (cipherfile) + rmtemp(cipherfile); + errorLvl = UNKNOWN_FILE_ERROR; + return -1; + } + + if (outfile == NULL) { /* file was not encrypted */ + if (!filter_mode && !moreflag) { + fprintf(pgpout,LANG("\007\nError: '%s' is not a ciphertext, signature, or key file.\n"), + cipherfile); + errorLvl = UNKNOWN_FILE_ERROR; + return -1; + } + outfile = cipherfile; + } else { + if (cipherfile) + rmtemp(cipherfile); + } + + if (moreflag || (strcmp(preserved_name,CONSOLE_FILENAME) == 0)) { + /* blort to screen */ + if (strcmp(preserved_name,CONSOLE_FILENAME) == 0) { + fprintf(pgpout, + LANG("\n\nThis message is marked \"For your eyes only\". Display now (Y/n)? ")); + if (batchmode || !getyesno('y')) { + /* no -- abort display, and clean up */ + rmtemp(outfile); + return 0; + } + } + if (!quietmode) + fprintf(pgpout, LANG("\n\nPlaintext message follows...\n")); + else + putc('\n', pgpout); + fprintf(pgpout, "------------------------------\n"); + more_file(outfile); + /* Disallow saving to disk if outfile is console-only: */ + if (strcmp(preserved_name,CONSOLE_FILENAME) == 0) { + clearscreen(); /* remove all evidence */ + } else if (!quietmode && !batchmode) { + fprintf(pgpout, LANG("Save this file permanently (y/N)? ")); + if (getyesno('n')) { + char moreFilename[256]; + fprintf(pgpout,LANG("Enter filename to save file as: ")); + if (preserved_name[0]) + fprintf(pgpout, "[%s]: ", file_tail(preserved_name)); + getstring( moreFilename, 255, TRUE ); + if (*moreFilename == '\0') { + if (*preserved_name != '\0') + savetemp (outfile, file_tail(preserved_name)); + else + rmtemp(outfile); + } + else + savetemp (outfile, moreFilename); + return 0; + } + } + rmtemp(outfile); + return 0; + } /* blort to screen */ + + if (outputfile) { + if (!strcmp(outputfile, "/dev/null")) { + rmtemp(outfile); + return 0; + } + filter_mode = (strcmp(outputfile, "-") == 0); + strcpy(plainfile, outputfile); + } else { +#ifdef VMS + /* VMS null extension has to be ".", not "" */ + force_extension(plainfile, "."); +#else /* not VMS */ + drop_extension(plainfile); +#endif /* not VMS */ + } + + if (!preserve_filename && filter_mode) { + if (writePhantomOutput(outfile) < 0) { + errorLvl = UNKNOWN_FILE_ERROR; + return -1; + } + rmtemp(outfile); + return 0; + } + + if (preserve_filename && preserved_name[0] != '\0') + strcpy(plainfile, file_tail(preserved_name)); + + if (quietmode) { + if (savetemp(outfile, plainfile) == NULL) { + errorLvl = UNKNOWN_FILE_ERROR; + return -1; + } + return 0; + } + if (!verbose) /* if other filename messages were suppressed */ + fprintf(pgpout,LANG("\nPlaintext filename: %s"), plainfile); + + + /*---------------------------------------------------------*/ + + /* One last thing-- let's attempt to classify some of the more + frequently occurring cases of plaintext output files, as an + aid to the user. + + For example, if output file is a public key, it should have + the right extension on the filename. + + Also, it will likely be common to encrypt files created by + various archivers, so they should be renamed with the archiver + extension. + */ + get_header_info_from_file( outfile, header, 8 ); + + newname = NULL; + if (header[0] == CTB_CERT_PUBKEY) { + /* Special case--may be public key, worth renaming */ + fprintf(pgpout, LANG("\nPlaintext file '%s' looks like it contains a public key."), + plainfile ); + newname = maybe_force_extension( plainfile, PGP_EXTENSION ); + } /* Possible public key output file */ + + else if ((i = compressSignature( header )) >= 0) { + /* Special case--may be an archived/compressed file, worth renaming */ + fprintf(pgpout, LANG("\nPlaintext file '%s' looks like a %s file."), + plainfile, compressName[i] ); + newname = maybe_force_extension( plainfile, compressExt[i] ); + } else if (is_ctb(header[0]) && + ( is_ctb_type (header[0], CTB_PKE_TYPE) + || is_ctb_type (header[0], CTB_SKE_TYPE) + || is_ctb_type (header[0], CTB_CKE_TYPE))) + { + /* Special case--may be another ciphertext file, worth renaming */ + fprintf(pgpout, LANG("\n\007Output file '%s' may contain more ciphertext or signature."), + plainfile ); + newname = maybe_force_extension( plainfile, PGP_EXTENSION ); + } /* Possible ciphertext output file */ + + if (savetemp(outfile, (newname ? newname : plainfile)) == NULL) { + errorLvl = UNKNOWN_FILE_ERROR; + return -1; + } + + fprintf (pgpout, "\n"); + return 0; +} /* do_decrypt */ + + +static int do_keyopt(char keychar) +{ + char keyfile[MAX_PATH]; + char ringfile[MAX_PATH]; + char *workfile; + int status; + + if ((filter_mode || batchmode) + && (keychar == 'g' || keychar == 'e' || keychar == 'd' + || (keychar == 'r' && sign_flag))) + { + errorLvl = NO_BATCH; + arg_error(); /* interactive process, no go in batch mode */ + } + + /* + * If we're not doing anything that uses stdout, produce output there, + * in case user wants to redirect it. + */ + if (!filter_mode) + pgpout = stdout; + + switch (keychar) { + + /*-------------------------------------------------------*/ + case 'g': + { /* Key generation + Arguments: bitcount, bitcount + */ + char keybits[6], ebits[6]; + + /* + * Why all this code? + * + * Some dimwits have been distributing versions of PGP (especially on + * MS-DOS) without the manuals. This frustrates users (who call + * Philip Zimmermann at all hours of the day and night), and in + * addition to depriving them of instructions on how to operate the + * program, also deprives them of IMPORTANT legal notices. This is + * a Bad Thing, so we've gone to the trouble of being fascist and + * *forcing* the manuals to be there. + * + * The +nomanual flag (documented only in the manual) lets you + * overrride this if desired. + */ + + if (!nomanual && manuals_missing()) { + char const * const *dir; + + fputs(LANG("\a\nError: PGP User's Guide not found.\n\ +PGP looked for it in the following directories:\n"), pgpout); + for (dir = manual_dirs; *dir; dir++) + fprintf(pgpout, "\t\"%s\"\n", *dir); + fputs(LANG("and the doc subdirectory of each of the above. Please put a copy of\n\ +both volumes of the User's Guide in one of these directories.\n\ +\n\ +Under NO CIRCUMSTANCES should PGP ever be distributed without the PGP\n\ +User's Guide, which is included in the standard distribution package.\n\ +If you got a copy of PGP without the manual, please inform whomever you\n\ +got it from that this is an incomplete package that should not be\n\ +distributed further.\n\ +\n\ +PGP will not generate a key without finding the User's Guide.\n\ +\n"), pgpout); + return KEYGEN_ERROR; + } + + if (myArgc > 2) + strncpy( keybits, myArgv[2], sizeof(keybits)-1 ); + else + keybits[0] = '\0'; + + if (myArgc > 3) + strncpy( ebits, myArgv[3], sizeof(ebits)-1 ); + else + ebits[0] = '\0'; + + /* dokeygen writes the keys out to the key rings... */ + status = dokeygen(keybits, ebits); + + if (status < 0) { + fprintf(pgpout, LANG("\007Keygen error. ") ); + errorLvl = KEYGEN_ERROR; + } + return status; + } /* Key generation */ + + /*-------------------------------------------------------*/ + case 'c': + { /* Key checking + Arguments: userid, ringfile + */ + + if (myArgc < 3) { /* Default to all user ID's */ + mcguffin[0] = '\0'; + } else { + strcpy ( mcguffin, myArgv[2] ); + if (strcmp( mcguffin, "*" ) == 0) + mcguffin[0] = '\0'; + } + CONVERT_TO_CANONICAL_CHARSET(mcguffin); + + if (myArgc < 4) /* default key ring filename */ + strcpy(ringfile, globalPubringName); + else + strncpy( ringfile, myArgv[3], sizeof(ringfile)-1 ); + + if ((myArgc < 4 && myArgc > 2) /* Allow just key file as arg */ + && has_extension( myArgv[2], PGP_EXTENSION ) ) + { + strcpy( ringfile, myArgv[2] ); + mcguffin[0] = '\0'; + } + + status = dokeycheck( mcguffin, ringfile, CHECK_ALL ); + + if (status < 0) { + fprintf(pgpout, LANG("\007Keyring check error.\n") ); + errorLvl = KEYRING_CHECK_ERROR; + } + if (status >= 0 && mcguffin[0] != '\0') + return status; /* just checking a single user, dont do maintenance */ + + if ((status = maint_check(ringfile, 0)) < 0 && status != -7) { + fprintf(pgpout, LANG("\007Maintenance pass error. ") ); + errorLvl = KEYRING_CHECK_ERROR; + } + + return status == -7 ? 0 : status; + } /* Key check */ + + /*-------------------------------------------------------*/ + case 'm': + { /* Maintenance pass + Arguments: ringfile + */ + + if (myArgc < 3) /* default key ring filename */ + strcpy(ringfile, globalPubringName); + else + strcpy( ringfile, myArgv[2] ); + +#ifdef MSDOS + strlwr( ringfile ); +#endif + if (! file_exists( ringfile )) + default_extension( ringfile, PGP_EXTENSION ); + + if ((status = maint_check(ringfile, + MAINT_VERBOSE|(c_flag ? MAINT_CHECK : 0))) < 0) + { + if (status == -7) + fprintf(pgpout, LANG("File '%s' is not a public keyring\n"), ringfile); + fprintf(pgpout, LANG("\007Maintenance pass error. ") ); + errorLvl = KEYRING_CHECK_ERROR; + } + return status; + } /* Maintenance pass */ + + /*-------------------------------------------------------*/ + case 's': + { /* Key signing + Arguments: her_id, keyfile + */ + + if (myArgc >= 4) + strncpy( keyfile, myArgv[3], sizeof(keyfile)-1 ); + else + strcpy(keyfile, globalPubringName); + + if (myArgc >= 3) { + strcpy( mcguffin, myArgv[2] ); /* Userid to sign */ + } else { + fprintf(pgpout, LANG("\nA user ID is required to select the public key you want to sign. ")); + if (batchmode) /* not interactive, userid must be on command line */ + return -1; + fprintf(pgpout, LANG("\nEnter the public key's user ID: ")); + getstring( mcguffin, 255, TRUE ); /* echo keyboard */ + } + CONVERT_TO_CANONICAL_CHARSET(mcguffin); + + if (my_name[0] == '\0') { + fprintf(pgpout, LANG("\nA secret key is required to make a signature. ")); + fprintf(pgpout, LANG("\nYou specified no user ID to select your secret key,\n\ +so the default user ID and key will be the most recently\n\ +added key on your secret keyring.\n")); + } + + status = signkey ( mcguffin, my_name, keyfile ); + + if (status >= 0) { + status = maint_update(keyfile, 0); + if (status == -7) { /* ringfile is a keyfile or secret keyring */ + fprintf(pgpout, "Warning: '%s' is not a public keyring\n", keyfile); + return 0; + } + if (status < 0) + fprintf(pgpout, LANG("\007Maintenance pass error. ") ); + } + + if (status < 0) { + fprintf(pgpout, LANG("\007Key signature error. ") ); + errorLvl = KEY_SIGNATURE_ERROR; + } + return status; + } /* Key signing */ + + + /*-------------------------------------------------------*/ + case 'd': + { /* disable/revoke key + Arguments: userid, keyfile + */ + + if (myArgc >= 4) + strncpy( keyfile, myArgv[3], sizeof(keyfile)-1 ); + else + strcpy(keyfile, globalPubringName); + + if (myArgc >= 3) { + strcpy( mcguffin, myArgv[2] ); /* Userid to sign */ + } else { + fprintf(pgpout, LANG("\nA user ID is required to select the key you want to revoke or disable. ")); + fprintf(pgpout, LANG("\nEnter user ID: ")); + getstring( mcguffin, 255, TRUE ); /* echo keyboard */ + } + CONVERT_TO_CANONICAL_CHARSET(mcguffin); + + status = disable_key ( mcguffin, keyfile ); + + if (status >= 0) { + status = maint_update(keyfile, 0); + if (status == -7) { /* ringfile is a keyfile or secret keyring */ + fprintf(pgpout, "Warning: '%s' is not a public keyring\n", keyfile); + return 0; + } + if (status < 0) + fprintf(pgpout, LANG("\007Maintenance pass error. ") ); + } + + if (status < 0) + errorLvl = KEY_SIGNATURE_ERROR; + return status; + } /* Key compromise */ + + /*-------------------------------------------------------*/ + case 'e': + { /* Key editing + Arguments: userid, ringfile + */ + + if (myArgc >= 4) + strncpy( ringfile, myArgv[3], sizeof(ringfile)-1 ); + else /* default key ring filename */ + strcpy(ringfile, globalPubringName); + + if (myArgc >= 3) { + strcpy( mcguffin, myArgv[2] ); /* Userid to edit */ + } else { + fprintf(pgpout, LANG("\nA user ID is required to select the key you want to edit. ")); + fprintf(pgpout, LANG("\nEnter the key's user ID: ")); + getstring( mcguffin, 255, TRUE ); /* echo keyboard */ + } + CONVERT_TO_CANONICAL_CHARSET(mcguffin); + + status = dokeyedit( mcguffin, ringfile ); + + if (status >= 0) { + status = maint_update(ringfile, 0); + if (status == -7) + status = 0; /* ignore "not a public keyring" error */ + if (status < 0) + fprintf(pgpout, LANG("\007Maintenance pass error. ") ); + } + + if (status < 0) { + fprintf(pgpout, LANG("\007Keyring edit error. ") ); + errorLvl = KEYRING_EDIT_ERROR; + } + return status; + } /* Key edit */ + + /*-------------------------------------------------------*/ + case 'a': + { /* Add key to key ring + Arguments: keyfile, ringfile + */ + + if (myArgc < 3 && !filter_mode) + arg_error(); + + if (!filter_mode) { /* Get the keyfile from args */ + strncpy( keyfile, myArgv[2], sizeof(keyfile)-1 ); + +#ifdef MSDOS + strlwr( keyfile ); +#endif + if (! file_exists( keyfile )) + default_extension( keyfile, PGP_EXTENSION ); + + if (! file_exists( keyfile )) { + fprintf(pgpout, LANG("\n\007Key file '%s' does not exist.\n"), keyfile ); + errorLvl = NONEXIST_KEY_ERROR; + return -1; + } + + workfile = keyfile; + + } else { + workfile = tempfile(TMP_WIPE|TMP_TMPDIR); + readPhantomInput(workfile); + } + + if (myArgc < (filter_mode ? 3 : 4)) { /* default key ring filename */ + byte ctb; + get_header_info_from_file(workfile, &ctb, 1); + if (ctb == CTB_CERT_SECKEY) + strcpy(ringfile, globalSecringName); + else + strcpy(ringfile, globalPubringName); + } else { + strncpy( ringfile, myArgv[(filter_mode ? 2 : 3)], sizeof(ringfile)-1 ); + default_extension( ringfile, PGP_EXTENSION ); + } +#ifdef MSDOS + strlwr( ringfile ); +#endif + + status = addto_keyring( workfile, ringfile); + + if (filter_mode) + rmtemp(workfile); + + if (status < 0) { + fprintf(pgpout, LANG("\007Keyring add error. ") ); + errorLvl = KEYRING_ADD_ERROR; + } + return status; + } /* Add key to key ring */ + + /*-------------------------------------------------------*/ + case 'x': + { /* Extract key from key ring + Arguments: mcguffin, keyfile, ringfile + */ + + if (myArgc >= (filter_mode ? 4 : 5)) /* default key ring filename */ + strncpy( ringfile, myArgv[(filter_mode ? 3 : 4)], sizeof(ringfile)-1 ); + else + strcpy(ringfile, globalPubringName); + + if (myArgc >= (filter_mode ? 2 : 3)) { + if (myArgv[2]) + /* Userid to extract */ + strcpy( mcguffin, myArgv[2] ); + else + strcpy( mcguffin, "" ); + } else { + fprintf(pgpout, LANG("\nA user ID is required to select the key you want to extract. ")); + if (batchmode) /* not interactive, userid must be on command line */ + return -1; + fprintf(pgpout, LANG("\nEnter the key's user ID: ")); + getstring( mcguffin, 255, TRUE ); /* echo keyboard */ + } + CONVERT_TO_CANONICAL_CHARSET(mcguffin); + + if (!filter_mode) { + if (myArgc >= 4) + strncpy(keyfile, myArgv[3], sizeof(keyfile)-1); + else + keyfile[0] = '\0'; + + workfile = keyfile; + } else { + workfile = tempfile(TMP_WIPE|TMP_TMPDIR); + } + +#ifdef MSDOS + strlwr( workfile ); + strlwr( ringfile ); +#endif + + default_extension( ringfile, PGP_EXTENSION ); + + status = extract_from_keyring( mcguffin, workfile, + ringfile, (filter_mode ? FALSE : + emit_radix_64) ); + + if (status < 0) { + fprintf(pgpout, LANG("\007Keyring extract error. ") ); + errorLvl = KEYRING_EXTRACT_ERROR; + if (filter_mode) + rmtemp(workfile); + return status; + } + + + if (filter_mode && !status) { + if (emit_radix_64) { + /* NULL for outputfile means write to stdout */ + if (armor_file(workfile, NULL, NULL, NULL) != 0) + { + errorLvl = UNKNOWN_FILE_ERROR; + return -1; + } + } else { + if (writePhantomOutput(workfile) < 0) { + errorLvl = UNKNOWN_FILE_ERROR; + return -1; + } + } + rmtemp(workfile); + } + + return 0; + } /* Extract key from key ring */ + + /*-------------------------------------------------------*/ + case 'r': + { /* Remove keys or selected key signatures from userid keys + Arguments: userid, ringfile + */ + + if (myArgc >= 4) + strcpy( ringfile, myArgv[3] ); + else /* default key ring filename */ + strcpy(ringfile, globalPubringName); + + if (myArgc >= 3) { + strcpy( mcguffin, myArgv[2] ); /* Userid to work on */ + } else { + if (sign_flag) { + fprintf(pgpout, LANG("\nA user ID is required to select the public key you want to\n\ +remove certifying signatures from. ")); + } else { + fprintf(pgpout, LANG("\nA user ID is required to select the key you want to remove. ")); + } + if (batchmode) /* not interactive, userid must be on command line */ + return -1; + fprintf(pgpout, LANG("\nEnter the key's user ID: ")); + getstring( mcguffin, 255, TRUE ); /* echo keyboard */ + } + CONVERT_TO_CANONICAL_CHARSET(mcguffin); + +#ifdef MSDOS + strlwr( ringfile ); +#endif + if (! file_exists( ringfile )) + default_extension( ringfile, PGP_EXTENSION ); + + if (sign_flag) { /* Remove signatures */ + if (remove_sigs( mcguffin, ringfile ) < 0) { + fprintf(pgpout, LANG("\007Key signature remove error. ") ); + errorLvl = KEYSIG_REMOVE_ERROR; + return -1; + } + } else { /* Remove keyring */ + if (remove_from_keyring( NULL, mcguffin, ringfile, (boolean) (myArgc < 4) ) < 0) + { + fprintf(pgpout, LANG("\007Keyring remove error. ") ); + errorLvl = KEYRING_REMOVE_ERROR; + return -1; + } + } + return 0; + } /* remove key signatures from userid */ + + /*-------------------------------------------------------*/ + case 'v': + case 'V': /* -kvv */ + { /* View or remove key ring entries, with userid match + Arguments: userid, ringfile + */ + + if (myArgc < 4) /* default key ring filename */ + strcpy(ringfile, globalPubringName); + else + strcpy( ringfile, myArgv[3] ); + + if (myArgc > 2) { + strcpy( mcguffin, myArgv[2] ); + if (strcmp( mcguffin, "*" ) == 0) + mcguffin[0] = '\0'; + } else { + *mcguffin = '\0'; + } + + if ((myArgc == 3) && has_extension( myArgv[2], PGP_EXTENSION )) + { + strcpy( ringfile, myArgv[2] ); + mcguffin[0] = '\0'; + } + CONVERT_TO_CANONICAL_CHARSET(mcguffin); + +#ifdef MSDOS + strlwr( ringfile ); +#endif + if (! file_exists( ringfile )) + default_extension( ringfile, PGP_EXTENSION ); + + /* If a second 'v' (keychar = V), show signatures too */ + status = view_keyring(mcguffin, ringfile, (boolean) (keychar == 'V'), c_flag); + if (status < 0) { + fprintf(pgpout, LANG("\007Keyring view error. ") ); + errorLvl = KEYRING_VIEW_ERROR; + } + return status; + } /* view key ring entries, with userid match */ + + default: + arg_error(); + } + return 0; +} /* do_keyopt */ + + + +void user_error() /* comes here if user made a boo-boo. */ +{ + fprintf(pgpout,LANG("\nFor a usage summary, type: pgp -h\n")); + fprintf(pgpout,LANG("For more detailed help, consult the PGP User's Guide.\n")); + exitPGP(errorLvl ? errorLvl : 127); /* error exit */ +} + +#if defined(DEBUG) && defined(linux) +#include +#endif +/* + * exitPGP: wipes and removes temporary files, also tries to wipe + * the stack. + */ +void exitPGP(int returnval) +{ + char buf[STACK_WIPE]; + struct hashedpw *hpw; + + if (verbose) + fprintf(pgpout, "exitPGP: exitcode = %d\n", returnval); + for (hpw = passwds; hpw; hpw = hpw->next) + memset(hpw->hash, 0, sizeof(hpw->hash)); + for (hpw = keypasswds; hpw; hpw = hpw->next) + memset(hpw->hash, 0, sizeof(hpw->hash)); + cleanup_tmpf(); +#if defined(DEBUG) && defined(linux) + if (verbose) { + struct mstats mstat; + mstat = mstats(); + printf("%d chunks used (%d bytes) %d bytes total\n", + mstat.chunks_used, mstat.bytes_used, mstat.bytes_total); + } +#endif + memset(buf, 0, sizeof(buf)); /* wipe stack */ +#ifdef VMS +/* + * Fake VMS style error returns with severity in bottom 3 bits + */ + if (returnval) + returnval = (returnval << 3) | 0x10000002; + else + returnval = 0x10000001; +#endif /* VMS */ + exit(returnval); +} + + +static void arg_error() +{ + signon_msg(); + fprintf(pgpout,LANG("\nInvalid arguments.\n")); + errorLvl = BAD_ARG_ERROR; + user_error(); +} + +/* + * Check for language specific help files in PGPPATH, then the system + * directory. If that fails, check for the default pgp.hlp, again + * firat a private copy, then the system-wide one. + * + * System-wide copies currently only exist on Unix. + */ +static void +build_helpfile(char *helpfile, char const *extra) +{ + if (strcmp(language, "en")) { + buildfilename(helpfile, language); + strcat(helpfile, extra); + force_extension(helpfile, HLP_EXTENSION); + if (file_exists(helpfile)) + return; +#ifdef PGP_SYSTEM_DIR + strcpy(helpfile, PGP_SYSTEM_DIR); + strcat(helpfile, language); + strcat(helpfile, extra); + force_extension(helpfile, HLP_EXTENSION); + if (file_exists(helpfile)) + return; +#endif + } + buildfilename(helpfile, "pgp"); + strcat(helpfile, extra); + force_extension(helpfile, HLP_EXTENSION); +#ifdef PGP_SYSTEM_DIR + if (file_exists(helpfile)) + return; + strcpy(helpfile, PGP_SYSTEM_DIR); + strcat(helpfile, "pgp"); + strcat(helpfile, extra); + force_extension(helpfile, HLP_EXTENSION); +#endif +} + +static void usage() +{ + char helpfile[MAX_PATH]; + char *tmphelp = helpfile; + extern unsigned char *ext_c_ptr; + + signon_msg(); + build_helpfile(helpfile, ""); + + if (ext_c_ptr) { + /* conversion to external format necessary */ + tmphelp = tempfile(TMP_TMPDIR); + CONVERSION = EXT_CONV; + if (copyfiles_by_name(helpfile, tmphelp) < 0) { + rmtemp(tmphelp); + tmphelp = helpfile; + } + CONVERSION = NO_CONV; + } + + /* built-in help if pgp.hlp is not available */ + if (more_file(tmphelp) < 0) + fprintf(pgpout,LANG("\nUsage summary:\ +\nTo encrypt a plaintext file with recipent's public key, type:\ +\n pgp -e textfile her_userid [other userids] (produces textfile.pgp)\ +\nTo sign a plaintext file with your secret key:\ +\n pgp -s textfile [-u your_userid] (produces textfile.pgp)\ +\nTo sign a plaintext file with your secret key, and then encrypt it\ +\n with recipent's public key, producing a .pgp file:\ +\n pgp -es textfile her_userid [other userids] [-u your_userid]\ +\nTo encrypt with conventional encryption only:\ +\n pgp -c textfile\ +\nTo decrypt or check a signature for a ciphertext (.pgp) file:\ +\n pgp ciphertextfile [plaintextfile]\ +\nTo produce output in ASCII for email, add the -a option to other options.\ +\nTo generate your own unique public/secret key pair: pgp -kg\ +\nFor help on other key management functions, type: pgp -k\n")); + if (ext_c_ptr) + rmtemp(tmphelp); + exit(BAD_ARG_ERROR); /* error exit */ +} + + +static void key_usage() +{ + char helpfile[MAX_PATH]; + char *tmphelp = helpfile; + extern unsigned char *ext_c_ptr; + + signon_msg(); + build_helpfile(helpfile, "key"); + + if (ext_c_ptr) { + /* conversion to external format necessary */ + tmphelp = tempfile(TMP_TMPDIR); + CONVERSION = EXT_CONV; + if (copyfiles_by_name(helpfile, tmphelp) < 0) { + rmtemp(tmphelp); + tmphelp = helpfile; + } + CONVERSION = NO_CONV; + } + + /* built-in help if pgp.hlp is not available */ + if (more_file(tmphelp) < 0) + /* only use built-in help if there is no helpfile */ + fprintf(pgpout,LANG("\nKey management functions:\ +\nTo generate your own unique public/secret key pair:\ +\n pgp -kg\ +\nTo add a key file's contents to your public or secret key ring:\ +\n pgp -ka keyfile [keyring]\ +\nTo remove a key or a user ID from your public or secret key ring:\ +\n pgp -kr userid [keyring]\ +\nTo edit your user ID or pass phrase:\ +\n pgp -ke your_userid [keyring]\ +\nTo extract (copy) a key from your public or secret key ring:\ +\n pgp -kx userid keyfile [keyring]\ +\nTo view the contents of your public key ring:\ +\n pgp -kv[v] [userid] [keyring]\ +\nTo check signatures on your public key ring:\ +\n pgp -kc [userid] [keyring]\ +\nTo sign someone else's public key on your public key ring:\ +\n pgp -ks her_userid [-u your_userid] [keyring]\ +\nTo remove selected signatures from a userid on a keyring:\ +\n pgp -krs userid [keyring]\ +\n")); + + exit(BAD_ARG_ERROR); /* error exit */ +}