1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-06-13 18:21:03 +02:00

gpg: Detect already compressed data also when using a pipe.

* common/iobuf.c (file_filter_ctx_t): Add fields for the peek feature.
(file_filter): Implement peeking.
(iobuf_ioctl): Add new IOBUF_IOCTL_PEEK.
* common/iobuf.h (IOBUF_IOCTL_PEEK, IOBUFCTRL_PEEK): New.
* common/miscellaneous.c (is_file_compressed): Rewrite.  Detect PDF.
* g10/encrypt.c (encrypt_simple): Peek before detecting compression.
(encrypt_crypt): Ditto.
* g10/sign.c (sign_file): Also detect already compressed data.

* g10/options.h (opt): Add explicit_compress_option.
* g10/gpg.c (main): Set opt.explicit_compress_option for -z.

--

Note that this patch also introduces a compression check for signing
which was never done in the past.

GnuPG-bug-id: 6332
Backported-from-master: 60963d98cfd8e60f88ee43c2d992f6dd3bbbd74c

Note that sign.c (sign_file) has been re-indented to ease future
backports.
This commit is contained in:
Werner Koch 2023-01-18 18:04:50 +01:00
parent ca822a2339
commit ce8ffd71b7
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
9 changed files with 491 additions and 347 deletions

View File

@ -1,7 +1,7 @@
/* iobuf.c - File Handling for OpenPGP. /* iobuf.c - File Handling for OpenPGP.
* Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008, * Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008,
* 2009, 2010, 2011 Free Software Foundation, Inc. * 2009, 2010, 2011 Free Software Foundation, Inc.
* Copyright (C) 2015 g10 Code GmbH * Copyright (C) 2015, 2023 g10 Code GmbH
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -93,6 +93,9 @@ typedef struct
int no_cache; int no_cache;
int eof_seen; int eof_seen;
int print_only_name; /* Flags indicating that fname is not a real file. */ int print_only_name; /* Flags indicating that fname is not a real file. */
char peeked[32]; /* Read ahead buffer. */
byte npeeked; /* Number of bytes valid in peeked. */
byte upeeked; /* Number of bytes used from peeked. */
char fname[1]; /* Name of the file. */ char fname[1]; /* Name of the file. */
} file_filter_ctx_t; } file_filter_ctx_t;
@ -454,7 +457,16 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
if (control == IOBUFCTRL_UNDERFLOW) if (control == IOBUFCTRL_UNDERFLOW)
{ {
log_assert (size); /* We need a buffer. */ log_assert (size); /* We need a buffer. */
if (a->eof_seen) if (a->npeeked > a->upeeked)
{
nbytes = a->npeeked - a->upeeked;
if (nbytes > size)
nbytes = size;
memcpy (buf, a->peeked + a->upeeked, nbytes);
a->upeeked += nbytes;
*ret_len = nbytes;
}
else if (a->eof_seen)
{ {
rc = -1; rc = -1;
*ret_len = 0; *ret_len = 0;
@ -573,6 +585,68 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
a->eof_seen = 0; a->eof_seen = 0;
a->keep_open = 0; a->keep_open = 0;
a->no_cache = 0; a->no_cache = 0;
a->npeeked = 0;
a->upeeked = 0;
}
else if (control == IOBUFCTRL_PEEK)
{
/* Peek on the input. */
#ifdef HAVE_W32_SYSTEM
unsigned long nread;
nbytes = 0;
if (!ReadFile (f, a->peeked, sizeof a->peeked, &nread, NULL))
{
int ec = (int) GetLastError ();
if (ec != ERROR_BROKEN_PIPE)
{
rc = gpg_error_from_errno (ec);
log_error ("%s: read error: ec=%d\n", a->fname, ec);
}
a->npeeked = 0;
}
else if (!nread)
{
a->eof_seen = 1;
a->npeeked = 0;
}
else
{
a->npeeked = nread;
}
#else /* Unix */
int n;
peek_more:
do
{
n = read (f, a->peeked + a->npeeked, sizeof a->peeked - a->npeeked);
}
while (n == -1 && errno == EINTR);
if (n > 0)
{
a->npeeked += n;
if (a->npeeked < sizeof a->peeked)
goto peek_more;
}
else if (!n) /* eof */
{
a->eof_seen = 1;
}
else /* error */
{
rc = gpg_error_from_syserror ();
if (gpg_err_code (rc) != GPG_ERR_EPIPE)
log_error ("%s: read error: %s\n", a->fname, gpg_strerror (rc));
}
#endif /* Unix */
size = a->npeeked < size? a->npeeked : size;
memcpy (buf, a->peeked, size);
*ret_len = size;
rc = 0; /* Return success - the user needs to check ret_len. */
} }
else if (control == IOBUFCTRL_DESC) else if (control == IOBUFCTRL_DESC)
{ {
@ -1487,6 +1561,25 @@ iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *ptrval)
return fd_cache_synchronize (ptrval); return fd_cache_synchronize (ptrval);
} }
} }
else if (cmd == IOBUF_IOCTL_PEEK)
{
/* Peek at a justed opened file. Use this only directly after a
* file has been opened for reading. Don't use it after you did
* a seek. This works only if just file filter has been
* pushed. Expects a buffer wit size INTVAL at PTRVAL and returns
* the number of bytes put into the buffer. */
if (DBG_IOBUF)
log_debug ("iobuf-%d.%d: ioctl '%s' peek\n",
a ? a->no : -1, a ? a->subno : -1, iobuf_desc (a, desc));
if (a->filter == file_filter && ptrval && intval)
{
file_filter_ctx_t *fcx = a->filter_ov;
size_t len = intval;
if (!file_filter (fcx, IOBUFCTRL_PEEK, NULL, ptrval, &len))
return (int)len;
}
}
return -1; return -1;

View File

@ -106,6 +106,7 @@ enum
IOBUFCTRL_FLUSH = 4, IOBUFCTRL_FLUSH = 4,
IOBUFCTRL_DESC = 5, IOBUFCTRL_DESC = 5,
IOBUFCTRL_CANCEL = 6, IOBUFCTRL_CANCEL = 6,
IOBUFCTRL_PEEK = 7,
IOBUFCTRL_USER = 16 IOBUFCTRL_USER = 16
}; };
@ -116,7 +117,8 @@ typedef enum
IOBUF_IOCTL_KEEP_OPEN = 1, /* Uses intval. */ IOBUF_IOCTL_KEEP_OPEN = 1, /* Uses intval. */
IOBUF_IOCTL_INVALIDATE_CACHE = 2, /* Uses ptrval. */ IOBUF_IOCTL_INVALIDATE_CACHE = 2, /* Uses ptrval. */
IOBUF_IOCTL_NO_CACHE = 3, /* Uses intval. */ IOBUF_IOCTL_NO_CACHE = 3, /* Uses intval. */
IOBUF_IOCTL_FSYNC = 4 /* Uses ptrval. */ IOBUF_IOCTL_FSYNC = 4, /* Uses ptrval. */
IOBUF_IOCTL_PEEK = 5 /* Uses intval and ptrval. */
} iobuf_ioctl_t; } iobuf_ioctl_t;
enum iobuf_use enum iobuf_use

View File

@ -466,7 +466,7 @@ decode_c_string (const char *src)
/* Check whether (BUF,LEN) is valid header for an OpenPGP compressed /* Check whether (BUF,LEN) is valid header for an OpenPGP compressed
* packet. LEN should be at least 6. */ * packet. LEN should be at least 6. */
static int static int
is_openpgp_compressed_packet (unsigned char *buf, size_t len) is_openpgp_compressed_packet (const unsigned char *buf, size_t len)
{ {
int c, ctb, pkttype; int c, ctb, pkttype;
int lenbytes; int lenbytes;
@ -508,63 +508,46 @@ is_openpgp_compressed_packet (unsigned char *buf, size_t len)
/* /*
* Check if the file is compressed. * Check if the file is compressed. You need to pass the first bytes
* of the file as (BUF,BUFLEN). Returns true if the buffer seems to
* be compressed.
*/ */
int int
is_file_compressed (const char *s, int *ret_rc) is_file_compressed (const byte *buf, unsigned int buflen)
{ {
iobuf_t a;
byte buf[6];
int i; int i;
int rc = 0;
int overflow;
struct magic_compress_s { struct magic_compress_s
size_t len; {
byte magic[4]; byte len;
} magic[] = { byte magic[5];
} magic[] =
{
{ 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */ { 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */
{ 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */ { 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */
{ 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */ { 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */
{ 5, { '%', 'P', 'D', 'F', '-'} } /* PDF */
}; };
if ( iobuf_is_pipe_filename (s) || !ret_rc ) if ( buflen < 6 )
return 0; /* We can't check stdin or no file was given */
a = iobuf_open( s );
if ( a == NULL ) {
*ret_rc = gpg_error_from_syserror ();
return 0;
}
iobuf_ioctl (a, IOBUF_IOCTL_NO_CACHE, 1, NULL);
if ( iobuf_get_filelength( a, &overflow ) < 6 && !overflow) {
*ret_rc = 0;
goto leave;
}
if ( iobuf_read( a, buf, 6 ) == -1 ) {
*ret_rc = a->error;
goto leave;
}
for ( i = 0; i < DIM( magic ); i++ ) {
if ( !memcmp( buf, magic[i].magic, magic[i].len ) ) {
*ret_rc = 0;
rc = 1;
goto leave;
}
}
if (is_openpgp_compressed_packet (buf, 6))
{ {
*ret_rc = 0; return 0; /* Too short to check - assume uncompressed. */
rc = 1;
} }
leave: for ( i = 0; i < DIM (magic); i++ )
iobuf_close( a ); {
return rc; if ( !memcmp( buf, magic[i].magic, magic[i].len ))
{
return 1; /* Is compressed. */
}
}
if (buflen >= 6 && is_openpgp_compressed_packet (buf, buflen))
{
return 1; /* Already compressed. */
}
return 0; /* Not detected as compressed. */
} }

View File

@ -347,7 +347,7 @@ char *try_make_printable_string (const void *p, size_t n, int delim);
char *make_printable_string (const void *p, size_t n, int delim); char *make_printable_string (const void *p, size_t n, int delim);
char *decode_c_string (const char *src); char *decode_c_string (const char *src);
int is_file_compressed (const char *s, int *ret_rc); int is_file_compressed (const byte *buf, unsigned int buflen);
int match_multistr (const char *multistr,const char *match); int match_multistr (const char *multistr,const char *match);

View File

@ -848,9 +848,10 @@ line.
@opindex keyedit:tsign @opindex keyedit:tsign
Make a trust signature. This is a signature that combines the notions Make a trust signature. This is a signature that combines the notions
of certification (like a regular signature), and trust (like the of certification (like a regular signature), and trust (like the
"trust" command). It is generally only useful in distinct communities "trust" command). It is generally useful in distinct communities
or groups. For more information please read the sections or groups to implement the concept of a Trusted Introducer. For
``Trust Signature'' and ``Regular Expression'' in RFC-4880. more information please read the sections ``Trust Signature'' and
``Regular Expression'' in RFC-4880.
@end table @end table
@c man:.RS @c man:.RS
@ -1619,6 +1620,16 @@ for the BZIP2 compression algorithm (defaulting to 6 as well). This is a
different option from @option{--compress-level} since BZIP2 uses a different option from @option{--compress-level} since BZIP2 uses a
significant amount of memory for each additional compression level. significant amount of memory for each additional compression level.
@option{-z} sets both. A value of 0 for @var{n} disables compression. @option{-z} sets both. A value of 0 for @var{n} disables compression.
A value of -1 forces compression using the default level.
Except for the @option{--store} command compression is always used
unless @command{gpg} detects that the input is already compressed. To
inhibit the use of compression use @option{-z0}; to force compression
use @option{-z-1} or option @option{z} with another compression level
than the default as indicated by -1. Note that this overriding of the
default deection works only with @option{z} and not with the long
variant of this option.
@item --bzip2-decompress-lowmem @item --bzip2-decompress-lowmem
@opindex bzip2-decompress-lowmem @opindex bzip2-decompress-lowmem

View File

@ -1,7 +1,7 @@
/* encrypt.c - Main encryption driver /* encrypt.c - Main encryption driver
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
* 2006, 2009 Free Software Foundation, Inc. * 2006, 2009 Free Software Foundation, Inc.
* Copyright (C) 2016, 2022 g10 Code GmbH * Copyright (C) 2016, 2022, 2023 g10 Code GmbH
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -17,6 +17,7 @@
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program; if not, see <https://www.gnu.org/licenses/>. * along with this program; if not, see <https://www.gnu.org/licenses/>.
* SPDX-License-Identifier: GPL-3.0-or-later
*/ */
#include <config.h> #include <config.h>
@ -326,6 +327,8 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
text_filter_context_t tfx; text_filter_context_t tfx;
progress_filter_context_t *pfx; progress_filter_context_t *pfx;
int do_compress = !!default_compress_algo(); int do_compress = !!default_compress_algo();
char peekbuf[32];
int peekbuflen;
if (!gnupg_rng_is_compliant (opt.compliance)) if (!gnupg_rng_is_compliant (opt.compliance))
{ {
@ -362,6 +365,14 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
return rc; return rc;
} }
peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf);
if (peekbuflen < 0)
{
peekbuflen = 0;
if (DBG_FILTER)
log_debug ("peeking at input failed\n");
}
handle_progress (pfx, inp, filename); handle_progress (pfx, inp, filename);
if (opt.textmode) if (opt.textmode)
@ -426,10 +437,11 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
if (do_compress if (do_compress
&& cfx.dek && cfx.dek
&& (cfx.dek->use_mdc || cfx.dek->use_aead) && (cfx.dek->use_mdc || cfx.dek->use_aead)
&& is_file_compressed(filename, &rc)) && !opt.explicit_compress_option
&& is_file_compressed (peekbuf, peekbuflen))
{ {
if (opt.verbose) if (opt.verbose)
log_info(_("'%s' already compressed\n"), filename); log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]");
do_compress = 0; do_compress = 0;
} }
@ -768,6 +780,8 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
progress_filter_context_t *pfx; progress_filter_context_t *pfx;
PK_LIST pk_list; PK_LIST pk_list;
int do_compress; int do_compress;
char peekbuf[32];
int peekbuflen;
if (filefd != -1 && filename) if (filefd != -1 && filename)
return gpg_error (GPG_ERR_INV_ARG); /* Both given. */ return gpg_error (GPG_ERR_INV_ARG); /* Both given. */
@ -840,6 +854,14 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
if (opt.verbose) if (opt.verbose)
log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp)); log_info (_("reading from '%s'\n"), iobuf_get_fname_nonnull (inp));
peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf);
if (peekbuflen < 0)
{
peekbuflen = 0;
if (DBG_FILTER)
log_debug ("peeking at input failed\n");
}
handle_progress (pfx, inp, filename); handle_progress (pfx, inp, filename);
if (opt.textmode) if (opt.textmode)
@ -874,10 +896,11 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
if (do_compress if (do_compress
&& (cfx.dek->use_mdc || cfx.dek->use_aead) && (cfx.dek->use_mdc || cfx.dek->use_aead)
&& is_file_compressed(filename, &rc2)) && !opt.explicit_compress_option
&& is_file_compressed (peekbuf, peekbuflen))
{ {
if (opt.verbose) if (opt.verbose)
log_info(_("'%s' already compressed\n"), filename); log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]");
do_compress = 0; do_compress = 0;
} }
if (rc2) if (rc2)

View File

@ -3126,6 +3126,7 @@ main (int argc, char **argv)
case oCompress: case oCompress:
/* this is the -z command line option */ /* this is the -z command line option */
opt.compress_level = opt.bz2_compress_level = pargs.r.ret_int; opt.compress_level = opt.bz2_compress_level = pargs.r.ret_int;
opt.explicit_compress_option = 1;
break; break;
case oCompressLevel: opt.compress_level = pargs.r.ret_int; break; case oCompressLevel: opt.compress_level = pargs.r.ret_int; break;
case oBZ2CompressLevel: opt.bz2_compress_level = pargs.r.ret_int; break; case oBZ2CompressLevel: opt.bz2_compress_level = pargs.r.ret_int; break;

View File

@ -92,6 +92,7 @@ struct
int force_ocb; int force_ocb;
int cert_digest_algo; int cert_digest_algo;
int compress_algo; int compress_algo;
int explicit_compress_option; /* A compress option was explicitly given. */
int compress_level; int compress_level;
int bz2_compress_level; int bz2_compress_level;
int bz2_decompress_lowmem; int bz2_decompress_lowmem;

View File

@ -928,7 +928,8 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
text_filter_context_t tfx; text_filter_context_t tfx;
progress_filter_context_t *pfx; progress_filter_context_t *pfx;
encrypt_filter_context_t efx; encrypt_filter_context_t efx;
IOBUF inp = NULL, out = NULL; iobuf_t inp = NULL;
iobuf_t out = NULL;
PACKET pkt; PACKET pkt;
int rc = 0; int rc = 0;
PK_LIST pk_list = NULL; PK_LIST pk_list = NULL;
@ -936,27 +937,31 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
SK_LIST sk_rover = NULL; SK_LIST sk_rover = NULL;
int multifile = 0; int multifile = 0;
u32 duration=0; u32 duration=0;
char peekbuf[32];
int peekbuflen = 0;
pfx = new_progress_context (); pfx = new_progress_context ();
afx = new_armor_context (); afx = new_armor_context ();
memset( &zfx, 0, sizeof zfx); memset (&zfx, 0, sizeof zfx);
memset( &mfx, 0, sizeof mfx); memset (&mfx, 0, sizeof mfx);
memset( &efx, 0, sizeof efx); memset (&efx, 0, sizeof efx);
efx.ctrl = ctrl; efx.ctrl = ctrl;
init_packet( &pkt ); init_packet (&pkt);
if( filenames ) { if (filenames)
{
fname = filenames->d; fname = filenames->d;
multifile = !!filenames->next; multifile = !!filenames->next;
} }
else else
fname = NULL; fname = NULL;
if( fname && filenames->next && (!detached || encryptflag) ) if (fname && filenames->next && (!detached || encryptflag))
log_bug("multiple files can only be detached signed"); log_bug ("multiple files can only be detached signed");
if(encryptflag==2 if (encryptflag == 2
&& (rc=setup_symkey(&efx.symkey_s2k,&efx.symkey_dek))) && (rc = setup_symkey (&efx.symkey_s2k,&efx.symkey_dek)))
goto leave; goto leave;
if (opt.ask_sig_expire && !opt.batch) if (opt.ask_sig_expire && !opt.batch)
@ -966,17 +971,18 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
/* Note: In the old non-agent version the following call used to /* Note: In the old non-agent version the following call used to
unprotect the secret key. This is now done on demand by the agent. */ unprotect the secret key. This is now done on demand by the agent. */
if( (rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG )) ) if ((rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG )))
goto leave; goto leave;
if (encryptflag if (encryptflag
&& (rc=build_pk_list (ctrl, remusr, &pk_list))) && (rc=build_pk_list (ctrl, remusr, &pk_list)))
goto leave; goto leave;
/* prepare iobufs */ /* Prepare iobufs. */
if( multifile ) /* have list of filenames */ if (multifile) /* have list of filenames */
inp = NULL; /* we do it later */ inp = NULL; /* we do it later */
else { else
{
inp = iobuf_open(fname); inp = iobuf_open(fname);
if (inp && is_secured_file (iobuf_get_fd (inp))) if (inp && is_secured_file (iobuf_get_fd (inp)))
{ {
@ -992,57 +998,67 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
goto leave; goto leave;
} }
peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf);
if (peekbuflen < 0)
{
peekbuflen = 0;
if (DBG_FILTER)
log_debug ("peeking at input failed\n");
}
handle_progress (pfx, inp, fname); handle_progress (pfx, inp, fname);
} }
if( outfile ) { if (outfile)
if (is_secured_filename ( outfile )) { {
if (is_secured_filename ( outfile ))
{
out = NULL; out = NULL;
gpg_err_set_errno (EPERM); gpg_err_set_errno (EPERM);
} }
else else
out = iobuf_create (outfile, 0); out = iobuf_create (outfile, 0);
if( !out ) if (!out)
{ {
rc = gpg_error_from_syserror (); rc = gpg_error_from_syserror ();
log_error(_("can't create '%s': %s\n"), outfile, strerror(errno) ); log_error(_("can't create '%s': %s\n"), outfile, strerror(errno) );
goto leave; goto leave;
} }
else if( opt.verbose ) else if (opt.verbose)
log_info(_("writing to '%s'\n"), outfile ); log_info (_("writing to '%s'\n"), outfile);
} }
else if( (rc = open_outfile (-1, fname, else if ((rc = open_outfile (-1, fname,
opt.armor? 1: detached? 2:0, 0, &out))) opt.armor? 1: detached? 2:0, 0, &out)))
goto leave; goto leave;
/* prepare to calculate the MD over the input */ /* Prepare to calculate the MD over the input. */
if( opt.textmode && !outfile && !multifile ) if (opt.textmode && !outfile && !multifile)
{ {
memset( &tfx, 0, sizeof tfx); memset( &tfx, 0, sizeof tfx);
iobuf_push_filter( inp, text_filter, &tfx ); iobuf_push_filter( inp, text_filter, &tfx );
} }
if ( gcry_md_open (&mfx.md, 0, 0) ) if (gcry_md_open (&mfx.md, 0, 0))
BUG (); BUG ();
if (DBG_HASHING) if (DBG_HASHING)
gcry_md_debug (mfx.md, "sign"); gcry_md_debug (mfx.md, "sign");
/* If we're encrypting and signing, it is reasonable to pick the /* If we're encrypting and signing, it is reasonable to pick the
hash algorithm to use out of the recipient key prefs. This is * hash algorithm to use out of the recipient key prefs. This is
best effort only, as in a DSA2 and smartcard world there are * best effort only, as in a DSA2 and smartcard world there are
cases where we cannot please everyone with a single hash (DSA2 * cases where we cannot please everyone with a single hash (DSA2
wants >160 and smartcards want =160). In the future this could * wants >160 and smartcards want =160). In the future this could
be more complex with different hashes for each sk, but the * be more complex with different hashes for each sk, but the
current design requires a single hash for all SKs. */ * current design requires a single hash for all SKs. */
if(pk_list) if (pk_list)
{ {
if(opt.def_digest_algo) if (opt.def_digest_algo)
{ {
if(!opt.expert && if (!opt.expert &&
select_algo_from_prefs(pk_list,PREFTYPE_HASH, select_algo_from_prefs(pk_list,PREFTYPE_HASH,
opt.def_digest_algo, opt.def_digest_algo,
NULL)!=opt.def_digest_algo) NULL)!=opt.def_digest_algo)
log_info(_("WARNING: forcing digest algorithm %s (%d)" log_info (_("WARNING: forcing digest algorithm %s (%d)"
" violates recipient preferences\n"), " violates recipient preferences\n"),
gcry_md_algo_name (opt.def_digest_algo), gcry_md_algo_name (opt.def_digest_algo),
opt.def_digest_algo ); opt.def_digest_algo );
@ -1054,22 +1070,21 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
struct pref_hint hint = { 0 }; struct pref_hint hint = { 0 };
/* Of course, if the recipient asks for something /* Of course, if the recipient asks for something
unreasonable (like the wrong hash for a DSA key) then * unreasonable (like the wrong hash for a DSA key) then
don't do it. Check all sk's - if any are DSA or live * don't do it. Check all sk's - if any are DSA or live
on a smartcard, then the hash has restrictions and we * on a smartcard, then the hash has restrictions and we
may not be able to give the recipient what they want. * may not be able to give the recipient what they want.
For DSA, pass a hint for the largest q we have. Note * For DSA, pass a hint for the largest q we have. Note
that this means that a q>160 key will override a q=160 * that this means that a q>160 key will override a q=160
key and force the use of truncation for the q=160 key. * key and force the use of truncation for the q=160 key.
The alternative would be to ignore the recipient prefs * The alternative would be to ignore the recipient prefs
completely and get a different hash for each DSA key in * completely and get a different hash for each DSA key in
hash_for(). The override behavior here is more or less * hash_for(). The override behavior here is more or less
reasonable as it is under the control of the user which * reasonable as it is under the control of the user which
keys they sign with for a given message and the fact * keys they sign with for a given message and the fact
that the message with multiple signatures won't be * that the message with multiple signatures won't be
usable on an implementation that doesn't understand * usable on an implementation that doesn't understand
DSA2 anyway. */ * DSA2 anyway. */
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next )
{ {
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA
@ -1093,10 +1108,9 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
temp_hashlen = (temp_hashlen+7)/8; temp_hashlen = (temp_hashlen+7)/8;
/* Pick a hash that is large enough for our /* Pick a hash that is large enough for our
largest q or matches our Q but if tehreare * largest q or matches our Q but if tehreare
several of them we run into a conflict and * several of them we run into a conflict and
don't use the preferences. */ * don't use the preferences. */
if (hint.digest_length < temp_hashlen) if (hint.digest_length < temp_hashlen)
{ {
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
@ -1123,16 +1137,17 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); gcry_md_enable (mfx.md, hash_for (sk_rover->pk));
if( !multifile ) if (!multifile)
iobuf_push_filter( inp, md_filter, &mfx ); iobuf_push_filter (inp, md_filter, &mfx);
if( detached && !encryptflag) if (detached && !encryptflag)
afx->what = 2; afx->what = 2;
if( opt.armor && !outfile ) if (opt.armor && !outfile)
push_armor_filter (afx, out); push_armor_filter (afx, out);
if( encryptflag ) { if (encryptflag)
{
efx.pk_list = pk_list; efx.pk_list = pk_list;
/* fixme: set efx.cfx.datalen if known */ /* fixme: set efx.cfx.datalen if known */
iobuf_push_filter( out, encrypt_filter, &efx ); iobuf_push_filter( out, encrypt_filter, &efx );
@ -1140,36 +1155,46 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
if (opt.compress_algo && !outfile && !detached) if (opt.compress_algo && !outfile && !detached)
{ {
int compr_algo=opt.compress_algo; int compr_algo = opt.compress_algo;
/* If not forced by user */ if (!opt.explicit_compress_option
if(compr_algo==-1) && is_file_compressed (peekbuf, peekbuflen))
{
if (opt.verbose)
log_info(_("'%s' already compressed\n"), fname? fname: "[stdin]");
compr_algo = 0;
}
else if (compr_algo==-1)
{ {
/* If we're not encrypting, then select_algo_from_prefs /* If we're not encrypting, then select_algo_from_prefs
will fail and we'll end up with the default. If we are * will fail and we'll end up with the default. If we are
encrypting, select_algo_from_prefs cannot fail since * encrypting, select_algo_from_prefs cannot fail since
there is an assumed preference for uncompressed data. * there is an assumed preference for uncompressed data.
Still, if it did fail, we'll also end up with the * Still, if it did fail, we'll also end up with the
default. */ * default. */
if ((compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP,
if((compr_algo= -1, NULL)) == -1)
select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1) {
compr_algo=default_compress_algo(); compr_algo = default_compress_algo();
} }
else if(!opt.expert && pk_list }
&& select_algo_from_prefs(pk_list,PREFTYPE_ZIP, else if (!opt.expert && pk_list
compr_algo,NULL)!=compr_algo) && select_algo_from_prefs (pk_list, PREFTYPE_ZIP,
log_info(_("WARNING: forcing compression algorithm %s (%d)" compr_algo, NULL) != compr_algo)
{
log_info (_("WARNING: forcing compression algorithm %s (%d)"
" violates recipient preferences\n"), " violates recipient preferences\n"),
compress_algo_to_string(compr_algo),compr_algo); compress_algo_to_string (compr_algo), compr_algo);
}
/* algo 0 means no compression */ /* Algo 0 means no compression. */
if( compr_algo ) if (compr_algo)
push_compress_filter(out,&zfx,compr_algo); push_compress_filter (out, &zfx, compr_algo);
} }
/* Write the one-pass signature packets if needed */ /* Write the one-pass signature packets if needed */
if (!detached) { if (!detached)
{
rc = write_onepass_sig_packets (sk_list, out, rc = write_onepass_sig_packets (sk_list, out,
opt.textmode && !outfile ? 0x01:0x00); opt.textmode && !outfile ? 0x01:0x00);
if (rc) if (rc)
@ -1179,15 +1204,18 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
write_status_begin_signing (mfx.md); write_status_begin_signing (mfx.md);
/* Setup the inner packet. */ /* Setup the inner packet. */
if( detached ) { if (detached)
if( multifile ) { {
if (multifile)
{
strlist_t sl; strlist_t sl;
if( opt.verbose ) if (opt.verbose)
log_info(_("signing:") ); log_info(_("signing:") );
/* must walk reverse trough this list */ /* Must walk reverse trough this list. */
for( sl = strlist_last(filenames); sl; for (sl = strlist_last (filenames); sl;
sl = strlist_prev( filenames, sl ) ) { sl = strlist_prev (filenames, sl))
{
inp = iobuf_open(sl->d); inp = iobuf_open(sl->d);
if (inp && is_secured_file (iobuf_get_fd (inp))) if (inp && is_secured_file (iobuf_get_fd (inp)))
{ {
@ -1195,7 +1223,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
inp = NULL; inp = NULL;
gpg_err_set_errno (EPERM); gpg_err_set_errno (EPERM);
} }
if( !inp ) if (!inp)
{ {
rc = gpg_error_from_syserror (); rc = gpg_error_from_syserror ();
log_error(_("can't open '%s': %s\n"), log_error(_("can't open '%s': %s\n"),
@ -1203,9 +1231,9 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
goto leave; goto leave;
} }
handle_progress (pfx, inp, sl->d); handle_progress (pfx, inp, sl->d);
if( opt.verbose ) if (opt.verbose)
log_printf (" '%s'", sl->d ); log_printf (" '%s'", sl->d );
if(opt.textmode) if (opt.textmode)
{ {
memset( &tfx, 0, sizeof tfx); memset( &tfx, 0, sizeof tfx);
iobuf_push_filter( inp, text_filter, &tfx ); iobuf_push_filter( inp, text_filter, &tfx );
@ -1215,37 +1243,40 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
; ;
iobuf_close(inp); inp = NULL; iobuf_close(inp); inp = NULL;
} }
if( opt.verbose ) if (opt.verbose)
log_printf ("\n"); log_printf ("\n");
} }
else { else
/* read, so that the filter can calculate the digest */ {
/* Read, so that the filter can calculate the digest. */
while (iobuf_read (inp, NULL, 1<<30) != -1 ) while (iobuf_read (inp, NULL, 1<<30) != -1 )
; ;
} }
} }
else { else
{
rc = write_plaintext_packet (out, inp, fname, rc = write_plaintext_packet (out, inp, fname,
opt.textmode && !outfile ? opt.textmode && !outfile ?
(opt.mimemode? 'm':'t'):'b'); (opt.mimemode? 'm':'t'):'b');
} }
/* catch errors from above */ /* Catch errors from above. */
if (rc) if (rc)
goto leave; goto leave;
/* write the signatures */ /* Write the signatures. */
rc = write_signature_packets (ctrl, sk_list, out, mfx.md, rc = write_signature_packets (ctrl, sk_list, out, mfx.md,
opt.textmode && !outfile? 0x01 : 0x00, opt.textmode && !outfile? 0x01 : 0x00,
0, duration, detached ? 'D':'S', NULL); 0, duration, detached ? 'D':'S', NULL);
if( rc ) if (rc)
goto leave; goto leave;
leave: leave:
if( rc ) if (rc)
iobuf_cancel(out); iobuf_cancel (out);
else { else
{
iobuf_close(out); iobuf_close(out);
if (encryptflag) if (encryptflag)
write_status( STATUS_END_ENCRYPTION ); write_status( STATUS_END_ENCRYPTION );
@ -1261,7 +1292,6 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
} }
/**************** /****************
* make a clear signature. note that opt.armor is not needed * make a clear signature. note that opt.armor is not needed
*/ */