Page MenuHome GnuPG

Intermittent output corruption using '-' for stdin
Closed, ResolvedPublic

Description

Release: 1.2.2

Environment

AMD K6-II 400, Redhat 8.0, bash script, 4096 bit key

Description

Creating a pipe command I used '-e -' to encode from stdin, the program didn't give any warnings about using -, and it seemed to work but occasionally produced unreadable output files.

Using - for stdin will occasionally produce a corrupt output file that can only be decrypted upto the first 4096 bytes.

Program determines if stdin is being used with code like this:
if (filename) /* testing for null */
{

/* stdin code */

}
else
{

/* explicit file name code */

}

However if '-' is used for stdin then filename is not NULL, it points to a string containing '-'.

The intermittent problem happens because of similar code in 'encode_simple' and 'encode_crypt':

    if( filename && !opt.textmode ) {
        off_t tmpsize;

	if ( !(tmpsize = iobuf_get_filelength(inp)) )
          log_info(_("%s: WARNING: empty file\n"), filename );
        /* We can't encode the length of very large files because
           OpenPGP uses only 32 bit for file sizes.  So if the the
           size of a file is larger than 2^32 minus some bytes for
           packet headers, we switch to partial length encoding. */
        if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
          filesize = tmpsize;
        else
          filesize = 0;
    }
    else
	filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */

The 'iobuf_get_filelength' call expects a file but when used on stdin (because '-' was used) it returns 0 sometimes other times it returns a number. I suspect this is a timing issue with pipes. Sometimes stdin will have pending bytes at this point, and sometimes not.

How To Repeat

Used with GNUPG source README file:

cat README | gpg -r user@somehost.com -o test.gpg -e -

Outputs message:

gpg: do_plaintext(): wrote 26446 bytes but expected 4096 bytes

When tyring to decrypt:
$ gpg -o test.txt -d test.gpg

You need a passphrase to unlock the secret key for
user: "xxxxx <xxxxx@xxxx>"
4096-bit ELG-E key, ID 8D8E8035, created 2003-05-12 (main key ID 867E58B2)

gpg: encrypted with 4096-bit ELG-E key, ID 8D8E8035, created 2003-05-12

"video <video@beast4>"

gpg: [don't know]: invalid packet (ctb=2d)
gpg: [don't know]: invalid packet (ctb=78)
gpg: WARNING: encrypted message has been manipulated!
gpg: [don't know]: invalid packet (ctb=2e)

Only first 4096 bytes of file are decrypted successfully.

Fix

In g10.c I changed the line in main():

fname = argc? *argv : NULL;

to:
if (argc > 0)
{

	if (strcmp(*argv, "-") != 0)
	{
	    fname = *argv;
	}
	else
	{
	    fname = NULL;
	}

else
{

	fname = NULL;

}

This seemed to work, however to keep using the official release I simply removed the '-' from the command line:

cat README | gpg -r user@somehost.com -o test.gpg -e

which works fine without changing code.

Release Note

Checked at that only valid filenames are passed to iobuf_get_filelength.

Thanks for this very detailed bug report.