1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-02-01 16:33:02 +01: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; int i;
byte buf[6];
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];
{ 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */ } magic[] =
{ 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */ {
{ 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */ { 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */
}; { 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */
{ 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 */ {
return 0; /* Too short to check - assume uncompressed. */
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 ) { for ( i = 0; i < DIM (magic); i++ )
*ret_rc = a->error; {
goto leave; if ( !memcmp( buf, magic[i].magic, magic[i].len ))
} {
return 1; /* Is compressed. */
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)) if (buflen >= 6 && is_openpgp_compressed_packet (buf, buflen))
{ {
*ret_rc = 0; return 1; /* Already compressed. */
rc = 1; }
}
leave: return 0; /* Not detected as compressed. */
iobuf_close( a );
return rc;
} }

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

@ -921,62 +921,68 @@ int
sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
int encryptflag, strlist_t remusr, const char *outfile ) int encryptflag, strlist_t remusr, const char *outfile )
{ {
const char *fname; const char *fname;
armor_filter_context_t *afx; armor_filter_context_t *afx;
compress_filter_context_t zfx; compress_filter_context_t zfx;
md_filter_context_t mfx; md_filter_context_t mfx;
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;
PACKET pkt; iobuf_t out = NULL;
int rc = 0; PACKET pkt;
PK_LIST pk_list = NULL; int rc = 0;
SK_LIST sk_list = NULL; PK_LIST pk_list = NULL;
SK_LIST sk_rover = NULL; SK_LIST sk_list = NULL;
int multifile = 0; SK_LIST sk_rover = NULL;
u32 duration=0; int multifile = 0;
u32 duration=0;
char peekbuf[32];
int peekbuflen = 0;
pfx = new_progress_context ();
afx = new_armor_context ();
memset( &zfx, 0, sizeof zfx);
memset( &mfx, 0, sizeof mfx);
memset( &efx, 0, sizeof efx);
efx.ctrl = ctrl;
init_packet( &pkt );
if( filenames ) { pfx = new_progress_context ();
fname = filenames->d; afx = new_armor_context ();
multifile = !!filenames->next; memset (&zfx, 0, sizeof zfx);
memset (&mfx, 0, sizeof mfx);
memset (&efx, 0, sizeof efx);
efx.ctrl = ctrl;
init_packet (&pkt);
if (filenames)
{
fname = filenames->d;
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)
duration = ask_expire_interval(1,opt.def_sig_expire); duration = ask_expire_interval(1,opt.def_sig_expire);
else else
duration = parse_expire_string(opt.def_sig_expire); duration = parse_expire_string(opt.def_sig_expire);
/* 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,274 +998,298 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
goto leave; goto leave;
} }
handle_progress (pfx, inp, fname); peekbuflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof peekbuf, peekbuf);
} if (peekbuflen < 0)
{
if( outfile ) { peekbuflen = 0;
if (is_secured_filename ( outfile )) { if (DBG_FILTER)
out = NULL; log_debug ("peeking at input failed\n");
gpg_err_set_errno (EPERM);
} }
else
out = iobuf_create (outfile, 0); handle_progress (pfx, inp, fname);
if( !out )
{
rc = gpg_error_from_syserror ();
log_error(_("can't create '%s': %s\n"), outfile, strerror(errno) );
goto leave;
}
else if( opt.verbose )
log_info(_("writing to '%s'\n"), outfile );
} }
else if( (rc = open_outfile (-1, fname,
opt.armor? 1: detached? 2:0, 0, &out)))
goto leave;
/* prepare to calculate the MD over the input */ if (outfile)
if( opt.textmode && !outfile && !multifile ) {
{ if (is_secured_filename ( outfile ))
memset( &tfx, 0, sizeof tfx); {
iobuf_push_filter( inp, text_filter, &tfx ); out = NULL;
} gpg_err_set_errno (EPERM);
}
else
out = iobuf_create (outfile, 0);
if (!out)
{
rc = gpg_error_from_syserror ();
log_error(_("can't create '%s': %s\n"), outfile, strerror(errno) );
goto leave;
}
else if (opt.verbose)
log_info (_("writing to '%s'\n"), outfile);
}
else if ((rc = open_outfile (-1, fname,
opt.armor? 1: detached? 2:0, 0, &out)))
goto leave;
if ( gcry_md_open (&mfx.md, 0, 0) ) /* Prepare to calculate the MD over the input. */
BUG (); if (opt.textmode && !outfile && !multifile)
if (DBG_HASHING) {
gcry_md_debug (mfx.md, "sign"); memset( &tfx, 0, sizeof tfx);
iobuf_push_filter( inp, text_filter, &tfx );
}
/* If we're encrypting and signing, it is reasonable to pick the if (gcry_md_open (&mfx.md, 0, 0))
hash algorithm to use out of the recipient key prefs. This is BUG ();
best effort only, as in a DSA2 and smartcard world there are if (DBG_HASHING)
cases where we cannot please everyone with a single hash (DSA2 gcry_md_debug (mfx.md, "sign");
wants >160 and smartcards want =160). In the future this could
be more complex with different hashes for each sk, but the
current design requires a single hash for all SKs. */
if(pk_list)
{
if(opt.def_digest_algo)
{
if(!opt.expert &&
select_algo_from_prefs(pk_list,PREFTYPE_HASH,
opt.def_digest_algo,
NULL)!=opt.def_digest_algo)
log_info(_("WARNING: forcing digest algorithm %s (%d)"
" violates recipient preferences\n"),
gcry_md_algo_name (opt.def_digest_algo),
opt.def_digest_algo );
}
else
{
int algo;
int conflict = 0;
struct pref_hint hint = { 0 };
/* Of course, if the recipient asks for something /* If we're encrypting and signing, it is reasonable to pick the
unreasonable (like the wrong hash for a DSA key) then * hash algorithm to use out of the recipient key prefs. This is
don't do it. Check all sk's - if any are DSA or live * best effort only, as in a DSA2 and smartcard world there are
on a smartcard, then the hash has restrictions and we * cases where we cannot please everyone with a single hash (DSA2
may not be able to give the recipient what they want. * wants >160 and smartcards want =160). In the future this could
For DSA, pass a hint for the largest q we have. Note * be more complex with different hashes for each sk, but the
that this means that a q>160 key will override a q=160 * current design requires a single hash for all SKs. */
key and force the use of truncation for the q=160 key. if (pk_list)
The alternative would be to ignore the recipient prefs {
completely and get a different hash for each DSA key in if (opt.def_digest_algo)
hash_for(). The override behavior here is more or less {
reasonable as it is under the control of the user which if (!opt.expert &&
keys they sign with for a given message and the fact select_algo_from_prefs(pk_list,PREFTYPE_HASH,
that the message with multiple signatures won't be opt.def_digest_algo,
usable on an implementation that doesn't understand NULL)!=opt.def_digest_algo)
DSA2 anyway. */ log_info (_("WARNING: forcing digest algorithm %s (%d)"
" violates recipient preferences\n"),
gcry_md_algo_name (opt.def_digest_algo),
opt.def_digest_algo );
}
else
{
int algo;
int conflict = 0;
struct pref_hint hint = { 0 };
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) /* Of course, if the recipient asks for something
{ * unreasonable (like the wrong hash for a DSA key) then
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA * don't do it. Check all sk's - if any are DSA or live
|| sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) * on a smartcard, then the hash has restrictions and we
{ * may not be able to give the recipient what they want.
int temp_hashlen = (gcry_mpi_get_nbits * For DSA, pass a hint for the largest q we have. Note
(sk_rover->pk->pkey[1])); * 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.
* The alternative would be to ignore the recipient prefs
* completely and get a different hash for each DSA key in
* hash_for(). The override behavior here is more or less
* reasonable as it is under the control of the user which
* keys they sign with for a given message and the fact
* that the message with multiple signatures won't be
* usable on an implementation that doesn't understand
* DSA2 anyway. */
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next )
{
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA
|| sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
{
int temp_hashlen = (gcry_mpi_get_nbits
(sk_rover->pk->pkey[1]));
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
{ {
temp_hashlen = ecdsa_qbits_from_Q (temp_hashlen); temp_hashlen = ecdsa_qbits_from_Q (temp_hashlen);
if (!temp_hashlen) if (!temp_hashlen)
conflict = 1; /* Better don't use the prefs. */ conflict = 1; /* Better don't use the prefs. */
temp_hashlen = (temp_hashlen+7)/8; temp_hashlen = (temp_hashlen+7)/8;
/* Fixup for that funny nistp521 (yes, 521) /* Fixup for that funny nistp521 (yes, 521)
* were we need to use a 512 bit hash algo. */ * were we need to use a 512 bit hash algo. */
if (temp_hashlen == 66) if (temp_hashlen == 66)
temp_hashlen = 64; temp_hashlen = 64;
} }
else else
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) {
{ if (hint.exact)
if (hint.exact) conflict = 1;
conflict = 1; hint.exact = 1;
hint.exact = 1; }
} hint.digest_length = temp_hashlen;
hint.digest_length = temp_hashlen; }
}
}
}
if (!conflict
&& (algo = select_algo_from_prefs (pk_list,PREFTYPE_HASH,
-1,&hint)) > 0)
{
/* Note that we later check that the algo is not weak. */
recipient_digest_algo = algo;
} }
} }
}
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) if (!conflict
gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); && (algo = select_algo_from_prefs (pk_list,PREFTYPE_HASH,
-1,&hint)) > 0)
if( !multifile ) {
iobuf_push_filter( inp, md_filter, &mfx ); /* Note that we later check that the algo is not weak. */
recipient_digest_algo = algo;
if( detached && !encryptflag) }
afx->what = 2; }
if( opt.armor && !outfile )
push_armor_filter (afx, out);
if( encryptflag ) {
efx.pk_list = pk_list;
/* fixme: set efx.cfx.datalen if known */
iobuf_push_filter( out, encrypt_filter, &efx );
} }
if (opt.compress_algo && !outfile && !detached) for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
{ gcry_md_enable (mfx.md, hash_for (sk_rover->pk));
int compr_algo=opt.compress_algo;
/* If not forced by user */ if (!multifile)
if(compr_algo==-1) iobuf_push_filter (inp, md_filter, &mfx);
{
/* If we're not encrypting, then select_algo_from_prefs
will fail and we'll end up with the default. If we are
encrypting, select_algo_from_prefs cannot fail since
there is an assumed preference for uncompressed data.
Still, if it did fail, we'll also end up with the
default. */
if((compr_algo= if (detached && !encryptflag)
select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1) afx->what = 2;
compr_algo=default_compress_algo();
}
else if(!opt.expert && pk_list
&& select_algo_from_prefs(pk_list,PREFTYPE_ZIP,
compr_algo,NULL)!=compr_algo)
log_info(_("WARNING: forcing compression algorithm %s (%d)"
" violates recipient preferences\n"),
compress_algo_to_string(compr_algo),compr_algo);
/* algo 0 means no compression */ if (opt.armor && !outfile)
if( compr_algo ) push_armor_filter (afx, out);
push_compress_filter(out,&zfx,compr_algo);
}
/* Write the one-pass signature packets if needed */ if (encryptflag)
if (!detached) { {
rc = write_onepass_sig_packets (sk_list, out, efx.pk_list = pk_list;
opt.textmode && !outfile ? 0x01:0x00); /* fixme: set efx.cfx.datalen if known */
if (rc) iobuf_push_filter( out, encrypt_filter, &efx );
goto leave;
} }
write_status_begin_signing (mfx.md); if (opt.compress_algo && !outfile && !detached)
{
int compr_algo = opt.compress_algo;
/* Setup the inner packet. */ if (!opt.explicit_compress_option
if( detached ) { && is_file_compressed (peekbuf, peekbuflen))
if( multifile ) { {
strlist_t sl; 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
* will fail and we'll end up with the default. If we are
* encrypting, select_algo_from_prefs cannot fail since
* there is an assumed preference for uncompressed data.
* Still, if it did fail, we'll also end up with the
* default. */
if ((compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP,
-1, NULL)) == -1)
{
compr_algo = default_compress_algo();
}
}
else if (!opt.expert && pk_list
&& select_algo_from_prefs (pk_list, PREFTYPE_ZIP,
compr_algo, NULL) != compr_algo)
{
log_info (_("WARNING: forcing compression algorithm %s (%d)"
" violates recipient preferences\n"),
compress_algo_to_string (compr_algo), compr_algo);
}
if( opt.verbose ) /* Algo 0 means no compression. */
log_info(_("signing:") ); if (compr_algo)
/* must walk reverse trough this list */ push_compress_filter (out, &zfx, compr_algo);
for( sl = strlist_last(filenames); sl;
sl = strlist_prev( filenames, sl ) ) {
inp = iobuf_open(sl->d);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if( !inp )
{
rc = gpg_error_from_syserror ();
log_error(_("can't open '%s': %s\n"),
sl->d,strerror(errno));
goto leave;
}
handle_progress (pfx, inp, sl->d);
if( opt.verbose )
log_printf (" '%s'", sl->d );
if(opt.textmode)
{
memset( &tfx, 0, sizeof tfx);
iobuf_push_filter( inp, text_filter, &tfx );
}
iobuf_push_filter( inp, md_filter, &mfx );
while (iobuf_read (inp, NULL, 1<<30) != -1 )
;
iobuf_close(inp); inp = NULL;
}
if( opt.verbose )
log_printf ("\n");
}
else {
/* read, so that the filter can calculate the digest */
while (iobuf_read (inp, NULL, 1<<30) != -1 )
;
}
}
else {
rc = write_plaintext_packet (out, inp, fname,
opt.textmode && !outfile ?
(opt.mimemode? 'm':'t'):'b');
} }
/* catch errors from above */ /* Write the one-pass signature packets if needed */
if (rc) if (!detached)
goto leave; {
rc = write_onepass_sig_packets (sk_list, out,
/* write the signatures */ opt.textmode && !outfile ? 0x01:0x00);
rc = write_signature_packets (ctrl, sk_list, out, mfx.md, if (rc)
opt.textmode && !outfile? 0x01 : 0x00,
0, duration, detached ? 'D':'S', NULL);
if( rc )
goto leave; goto leave;
leave:
if( rc )
iobuf_cancel(out);
else {
iobuf_close(out);
if (encryptflag)
write_status( STATUS_END_ENCRYPTION );
} }
iobuf_close(inp);
gcry_md_close ( mfx.md );
release_sk_list( sk_list );
release_pk_list( pk_list );
recipient_digest_algo=0;
release_progress_context (pfx);
release_armor_context (afx);
return rc;
}
write_status_begin_signing (mfx.md);
/* Setup the inner packet. */
if (detached)
{
if (multifile)
{
strlist_t sl;
if (opt.verbose)
log_info(_("signing:") );
/* Must walk reverse trough this list. */
for (sl = strlist_last (filenames); sl;
sl = strlist_prev (filenames, sl))
{
inp = iobuf_open(sl->d);
if (inp && is_secured_file (iobuf_get_fd (inp)))
{
iobuf_close (inp);
inp = NULL;
gpg_err_set_errno (EPERM);
}
if (!inp)
{
rc = gpg_error_from_syserror ();
log_error(_("can't open '%s': %s\n"),
sl->d,strerror(errno));
goto leave;
}
handle_progress (pfx, inp, sl->d);
if (opt.verbose)
log_printf (" '%s'", sl->d );
if (opt.textmode)
{
memset( &tfx, 0, sizeof tfx);
iobuf_push_filter( inp, text_filter, &tfx );
}
iobuf_push_filter( inp, md_filter, &mfx );
while (iobuf_read (inp, NULL, 1<<30) != -1 )
;
iobuf_close(inp); inp = NULL;
}
if (opt.verbose)
log_printf ("\n");
}
else
{
/* Read, so that the filter can calculate the digest. */
while (iobuf_read (inp, NULL, 1<<30) != -1 )
;
}
}
else
{
rc = write_plaintext_packet (out, inp, fname,
opt.textmode && !outfile ?
(opt.mimemode? 'm':'t'):'b');
}
/* Catch errors from above. */
if (rc)
goto leave;
/* Write the signatures. */
rc = write_signature_packets (ctrl, sk_list, out, mfx.md,
opt.textmode && !outfile? 0x01 : 0x00,
0, duration, detached ? 'D':'S', NULL);
if (rc)
goto leave;
leave:
if (rc)
iobuf_cancel (out);
else
{
iobuf_close(out);
if (encryptflag)
write_status( STATUS_END_ENCRYPTION );
}
iobuf_close(inp);
gcry_md_close ( mfx.md );
release_sk_list( sk_list );
release_pk_list( pk_list );
recipient_digest_algo=0;
release_progress_context (pfx);
release_armor_context (afx);
return rc;
}
/**************** /****************