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:
parent
ca822a2339
commit
ce8ffd71b7
@ -1,7 +1,7 @@
|
||||
/* iobuf.c - File Handling for OpenPGP.
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2003, 2004, 2006, 2007, 2008,
|
||||
* 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.
|
||||
*
|
||||
@ -93,6 +93,9 @@ typedef struct
|
||||
int no_cache;
|
||||
int eof_seen;
|
||||
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. */
|
||||
} file_filter_ctx_t;
|
||||
|
||||
@ -454,7 +457,16 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
|
||||
if (control == IOBUFCTRL_UNDERFLOW)
|
||||
{
|
||||
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;
|
||||
*ret_len = 0;
|
||||
@ -573,6 +585,68 @@ file_filter (void *opaque, int control, iobuf_t chain, byte * buf,
|
||||
a->eof_seen = 0;
|
||||
a->keep_open = 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)
|
||||
{
|
||||
@ -1487,6 +1561,25 @@ iobuf_ioctl (iobuf_t a, iobuf_ioctl_t cmd, int intval, void *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;
|
||||
|
@ -106,6 +106,7 @@ enum
|
||||
IOBUFCTRL_FLUSH = 4,
|
||||
IOBUFCTRL_DESC = 5,
|
||||
IOBUFCTRL_CANCEL = 6,
|
||||
IOBUFCTRL_PEEK = 7,
|
||||
IOBUFCTRL_USER = 16
|
||||
};
|
||||
|
||||
@ -116,7 +117,8 @@ typedef enum
|
||||
IOBUF_IOCTL_KEEP_OPEN = 1, /* Uses intval. */
|
||||
IOBUF_IOCTL_INVALIDATE_CACHE = 2, /* Uses ptrval. */
|
||||
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;
|
||||
|
||||
enum iobuf_use
|
||||
|
@ -466,7 +466,7 @@ decode_c_string (const char *src)
|
||||
/* Check whether (BUF,LEN) is valid header for an OpenPGP compressed
|
||||
* packet. LEN should be at least 6. */
|
||||
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 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
|
||||
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 rc = 0;
|
||||
int overflow;
|
||||
int i;
|
||||
|
||||
struct magic_compress_s {
|
||||
size_t len;
|
||||
byte magic[4];
|
||||
} magic[] = {
|
||||
{ 3, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */
|
||||
{ 3, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */
|
||||
{ 4, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */
|
||||
};
|
||||
struct magic_compress_s
|
||||
{
|
||||
byte len;
|
||||
byte magic[5];
|
||||
} magic[] =
|
||||
{
|
||||
{ 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 )
|
||||
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 ( buflen < 6 )
|
||||
{
|
||||
return 0; /* Too short to check - assume uncompressed. */
|
||||
}
|
||||
|
||||
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;
|
||||
for ( i = 0; i < DIM (magic); i++ )
|
||||
{
|
||||
if ( !memcmp( buf, magic[i].magic, magic[i].len ))
|
||||
{
|
||||
return 1; /* Is compressed. */
|
||||
}
|
||||
}
|
||||
|
||||
if (is_openpgp_compressed_packet (buf, 6))
|
||||
{
|
||||
*ret_rc = 0;
|
||||
rc = 1;
|
||||
}
|
||||
if (buflen >= 6 && is_openpgp_compressed_packet (buf, buflen))
|
||||
{
|
||||
return 1; /* Already compressed. */
|
||||
}
|
||||
|
||||
leave:
|
||||
iobuf_close( a );
|
||||
return rc;
|
||||
return 0; /* Not detected as compressed. */
|
||||
}
|
||||
|
||||
|
||||
|
@ -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 *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);
|
||||
|
||||
|
17
doc/gpg.texi
17
doc/gpg.texi
@ -848,9 +848,10 @@ line.
|
||||
@opindex keyedit:tsign
|
||||
Make a trust signature. This is a signature that combines the notions
|
||||
of certification (like a regular signature), and trust (like the
|
||||
"trust" command). It is generally only useful in distinct communities
|
||||
or groups. For more information please read the sections
|
||||
``Trust Signature'' and ``Regular Expression'' in RFC-4880.
|
||||
"trust" command). It is generally useful in distinct communities
|
||||
or groups to implement the concept of a Trusted Introducer. For
|
||||
more information please read the sections ``Trust Signature'' and
|
||||
``Regular Expression'' in RFC-4880.
|
||||
@end table
|
||||
|
||||
@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
|
||||
significant amount of memory for each additional compression level.
|
||||
@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
|
||||
@opindex bzip2-decompress-lowmem
|
||||
|
@ -1,7 +1,7 @@
|
||||
/* encrypt.c - Main encryption driver
|
||||
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
|
||||
* 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.
|
||||
*
|
||||
@ -17,6 +17,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
@ -326,6 +327,8 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
|
||||
text_filter_context_t tfx;
|
||||
progress_filter_context_t *pfx;
|
||||
int do_compress = !!default_compress_algo();
|
||||
char peekbuf[32];
|
||||
int peekbuflen;
|
||||
|
||||
if (!gnupg_rng_is_compliant (opt.compliance))
|
||||
{
|
||||
@ -362,6 +365,14 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
|
||||
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);
|
||||
|
||||
if (opt.textmode)
|
||||
@ -426,10 +437,11 @@ encrypt_simple (const char *filename, int mode, int use_seskey)
|
||||
if (do_compress
|
||||
&& cfx.dek
|
||||
&& (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)
|
||||
log_info(_("'%s' already compressed\n"), filename);
|
||||
log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]");
|
||||
do_compress = 0;
|
||||
}
|
||||
|
||||
@ -768,6 +780,8 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
|
||||
progress_filter_context_t *pfx;
|
||||
PK_LIST pk_list;
|
||||
int do_compress;
|
||||
char peekbuf[32];
|
||||
int peekbuflen;
|
||||
|
||||
if (filefd != -1 && filename)
|
||||
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)
|
||||
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);
|
||||
|
||||
if (opt.textmode)
|
||||
@ -874,10 +896,11 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
|
||||
|
||||
if (do_compress
|
||||
&& (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)
|
||||
log_info(_("'%s' already compressed\n"), filename);
|
||||
log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]");
|
||||
do_compress = 0;
|
||||
}
|
||||
if (rc2)
|
||||
|
@ -3126,6 +3126,7 @@ main (int argc, char **argv)
|
||||
case oCompress:
|
||||
/* this is the -z command line option */
|
||||
opt.compress_level = opt.bz2_compress_level = pargs.r.ret_int;
|
||||
opt.explicit_compress_option = 1;
|
||||
break;
|
||||
case oCompressLevel: opt.compress_level = pargs.r.ret_int; break;
|
||||
case oBZ2CompressLevel: opt.bz2_compress_level = pargs.r.ret_int; break;
|
||||
|
@ -92,6 +92,7 @@ struct
|
||||
int force_ocb;
|
||||
int cert_digest_algo;
|
||||
int compress_algo;
|
||||
int explicit_compress_option; /* A compress option was explicitly given. */
|
||||
int compress_level;
|
||||
int bz2_compress_level;
|
||||
int bz2_decompress_lowmem;
|
||||
|
606
g10/sign.c
606
g10/sign.c
@ -921,62 +921,68 @@ int
|
||||
sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
|
||||
int encryptflag, strlist_t remusr, const char *outfile )
|
||||
{
|
||||
const char *fname;
|
||||
armor_filter_context_t *afx;
|
||||
compress_filter_context_t zfx;
|
||||
md_filter_context_t mfx;
|
||||
text_filter_context_t tfx;
|
||||
progress_filter_context_t *pfx;
|
||||
encrypt_filter_context_t efx;
|
||||
IOBUF inp = NULL, out = NULL;
|
||||
PACKET pkt;
|
||||
int rc = 0;
|
||||
PK_LIST pk_list = NULL;
|
||||
SK_LIST sk_list = NULL;
|
||||
SK_LIST sk_rover = NULL;
|
||||
int multifile = 0;
|
||||
u32 duration=0;
|
||||
const char *fname;
|
||||
armor_filter_context_t *afx;
|
||||
compress_filter_context_t zfx;
|
||||
md_filter_context_t mfx;
|
||||
text_filter_context_t tfx;
|
||||
progress_filter_context_t *pfx;
|
||||
encrypt_filter_context_t efx;
|
||||
iobuf_t inp = NULL;
|
||||
iobuf_t out = NULL;
|
||||
PACKET pkt;
|
||||
int rc = 0;
|
||||
PK_LIST pk_list = NULL;
|
||||
SK_LIST sk_list = NULL;
|
||||
SK_LIST sk_rover = NULL;
|
||||
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 ) {
|
||||
fname = filenames->d;
|
||||
multifile = !!filenames->next;
|
||||
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)
|
||||
{
|
||||
fname = filenames->d;
|
||||
multifile = !!filenames->next;
|
||||
}
|
||||
else
|
||||
fname = NULL;
|
||||
else
|
||||
fname = NULL;
|
||||
|
||||
if( fname && filenames->next && (!detached || encryptflag) )
|
||||
log_bug("multiple files can only be detached signed");
|
||||
if (fname && filenames->next && (!detached || encryptflag))
|
||||
log_bug ("multiple files can only be detached signed");
|
||||
|
||||
if(encryptflag==2
|
||||
&& (rc=setup_symkey(&efx.symkey_s2k,&efx.symkey_dek)))
|
||||
goto leave;
|
||||
if (encryptflag == 2
|
||||
&& (rc = setup_symkey (&efx.symkey_s2k,&efx.symkey_dek)))
|
||||
goto leave;
|
||||
|
||||
if (opt.ask_sig_expire && !opt.batch)
|
||||
duration = ask_expire_interval(1,opt.def_sig_expire);
|
||||
else
|
||||
duration = parse_expire_string(opt.def_sig_expire);
|
||||
if (opt.ask_sig_expire && !opt.batch)
|
||||
duration = ask_expire_interval(1,opt.def_sig_expire);
|
||||
else
|
||||
duration = parse_expire_string(opt.def_sig_expire);
|
||||
|
||||
/* 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. */
|
||||
if( (rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG )) )
|
||||
goto leave;
|
||||
/* 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. */
|
||||
if ((rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG )))
|
||||
goto leave;
|
||||
|
||||
if (encryptflag
|
||||
&& (rc=build_pk_list (ctrl, remusr, &pk_list)))
|
||||
goto leave;
|
||||
if (encryptflag
|
||||
&& (rc=build_pk_list (ctrl, remusr, &pk_list)))
|
||||
goto leave;
|
||||
|
||||
/* prepare iobufs */
|
||||
if( multifile ) /* have list of filenames */
|
||||
inp = NULL; /* we do it later */
|
||||
else {
|
||||
/* Prepare iobufs. */
|
||||
if (multifile) /* have list of filenames */
|
||||
inp = NULL; /* we do it later */
|
||||
else
|
||||
{
|
||||
inp = iobuf_open(fname);
|
||||
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;
|
||||
}
|
||||
|
||||
handle_progress (pfx, inp, fname);
|
||||
}
|
||||
|
||||
if( outfile ) {
|
||||
if (is_secured_filename ( outfile )) {
|
||||
out = NULL;
|
||||
gpg_err_set_errno (EPERM);
|
||||
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");
|
||||
}
|
||||
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 );
|
||||
|
||||
handle_progress (pfx, inp, fname);
|
||||
}
|
||||
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( opt.textmode && !outfile && !multifile )
|
||||
{
|
||||
memset( &tfx, 0, sizeof tfx);
|
||||
iobuf_push_filter( inp, text_filter, &tfx );
|
||||
}
|
||||
if (outfile)
|
||||
{
|
||||
if (is_secured_filename ( outfile ))
|
||||
{
|
||||
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) )
|
||||
BUG ();
|
||||
if (DBG_HASHING)
|
||||
gcry_md_debug (mfx.md, "sign");
|
||||
/* Prepare to calculate the MD over the input. */
|
||||
if (opt.textmode && !outfile && !multifile)
|
||||
{
|
||||
memset( &tfx, 0, sizeof tfx);
|
||||
iobuf_push_filter( inp, text_filter, &tfx );
|
||||
}
|
||||
|
||||
/* If we're encrypting and signing, it is reasonable to pick the
|
||||
hash algorithm to use out of the recipient key prefs. This is
|
||||
best effort only, as in a DSA2 and smartcard world there are
|
||||
cases where we cannot please everyone with a single hash (DSA2
|
||||
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 };
|
||||
if (gcry_md_open (&mfx.md, 0, 0))
|
||||
BUG ();
|
||||
if (DBG_HASHING)
|
||||
gcry_md_debug (mfx.md, "sign");
|
||||
|
||||
/* Of course, if the recipient asks for something
|
||||
unreasonable (like the wrong hash for a DSA key) then
|
||||
don't do it. Check all sk's - if any are DSA or live
|
||||
on a smartcard, then the hash has restrictions and we
|
||||
may not be able to give the recipient what they want.
|
||||
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
|
||||
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. */
|
||||
/* If we're encrypting and signing, it is reasonable to pick the
|
||||
* hash algorithm to use out of the recipient key prefs. This is
|
||||
* best effort only, as in a DSA2 and smartcard world there are
|
||||
* cases where we cannot please everyone with a single hash (DSA2
|
||||
* 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 };
|
||||
|
||||
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]));
|
||||
/* Of course, if the recipient asks for something
|
||||
* unreasonable (like the wrong hash for a DSA key) then
|
||||
* don't do it. Check all sk's - if any are DSA or live
|
||||
* on a smartcard, then the hash has restrictions and we
|
||||
* may not be able to give the recipient what they want.
|
||||
* 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
|
||||
* 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)
|
||||
{
|
||||
temp_hashlen = ecdsa_qbits_from_Q (temp_hashlen);
|
||||
if (!temp_hashlen)
|
||||
conflict = 1; /* Better don't use the prefs. */
|
||||
temp_hashlen = (temp_hashlen+7)/8;
|
||||
/* Fixup for that funny nistp521 (yes, 521)
|
||||
* were we need to use a 512 bit hash algo. */
|
||||
if (temp_hashlen == 66)
|
||||
temp_hashlen = 64;
|
||||
}
|
||||
else
|
||||
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
|
||||
{
|
||||
temp_hashlen = ecdsa_qbits_from_Q (temp_hashlen);
|
||||
if (!temp_hashlen)
|
||||
conflict = 1; /* Better don't use the prefs. */
|
||||
temp_hashlen = (temp_hashlen+7)/8;
|
||||
/* Fixup for that funny nistp521 (yes, 521)
|
||||
* were we need to use a 512 bit hash algo. */
|
||||
if (temp_hashlen == 66)
|
||||
temp_hashlen = 64;
|
||||
}
|
||||
else
|
||||
temp_hashlen = (temp_hashlen+7)/8;
|
||||
|
||||
/* Pick a hash that is large enough for our
|
||||
largest q or matches our Q but if tehreare
|
||||
several of them we run into a conflict and
|
||||
don't use the preferences. */
|
||||
|
||||
if (hint.digest_length < temp_hashlen)
|
||||
{
|
||||
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
|
||||
{
|
||||
if (hint.exact)
|
||||
conflict = 1;
|
||||
hint.exact = 1;
|
||||
}
|
||||
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;
|
||||
/* Pick a hash that is large enough for our
|
||||
* largest q or matches our Q but if tehreare
|
||||
* several of them we run into a conflict and
|
||||
* don't use the preferences. */
|
||||
if (hint.digest_length < temp_hashlen)
|
||||
{
|
||||
if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA)
|
||||
{
|
||||
if (hint.exact)
|
||||
conflict = 1;
|
||||
hint.exact = 1;
|
||||
}
|
||||
hint.digest_length = temp_hashlen;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
|
||||
gcry_md_enable (mfx.md, hash_for (sk_rover->pk));
|
||||
|
||||
if( !multifile )
|
||||
iobuf_push_filter( inp, md_filter, &mfx );
|
||||
|
||||
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 (!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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opt.compress_algo && !outfile && !detached)
|
||||
{
|
||||
int compr_algo=opt.compress_algo;
|
||||
for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next)
|
||||
gcry_md_enable (mfx.md, hash_for (sk_rover->pk));
|
||||
|
||||
/* If not forced by user */
|
||||
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 (!multifile)
|
||||
iobuf_push_filter (inp, md_filter, &mfx);
|
||||
|
||||
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 (detached && !encryptflag)
|
||||
afx->what = 2;
|
||||
|
||||
/* algo 0 means no compression */
|
||||
if( compr_algo )
|
||||
push_compress_filter(out,&zfx,compr_algo);
|
||||
}
|
||||
if (opt.armor && !outfile)
|
||||
push_armor_filter (afx, out);
|
||||
|
||||
/* Write the one-pass signature packets if needed */
|
||||
if (!detached) {
|
||||
rc = write_onepass_sig_packets (sk_list, out,
|
||||
opt.textmode && !outfile ? 0x01:0x00);
|
||||
if (rc)
|
||||
goto leave;
|
||||
if (encryptflag)
|
||||
{
|
||||
efx.pk_list = pk_list;
|
||||
/* fixme: set efx.cfx.datalen if known */
|
||||
iobuf_push_filter( out, encrypt_filter, &efx );
|
||||
}
|
||||
|
||||
write_status_begin_signing (mfx.md);
|
||||
if (opt.compress_algo && !outfile && !detached)
|
||||
{
|
||||
int compr_algo = opt.compress_algo;
|
||||
|
||||
/* Setup the inner packet. */
|
||||
if( detached ) {
|
||||
if( multifile ) {
|
||||
strlist_t sl;
|
||||
if (!opt.explicit_compress_option
|
||||
&& 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
|
||||
* 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 )
|
||||
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');
|
||||
/* Algo 0 means no compression. */
|
||||
if (compr_algo)
|
||||
push_compress_filter (out, &zfx, compr_algo);
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
/* Write the one-pass signature packets if needed */
|
||||
if (!detached)
|
||||
{
|
||||
rc = write_onepass_sig_packets (sk_list, out,
|
||||
opt.textmode && !outfile ? 0x01:0x00);
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
/****************
|
||||
|
Loading…
x
Reference in New Issue
Block a user