From 2f872fa68c6576724b9dabee9fb0844266f55d0d Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Wed, 24 May 2023 10:36:04 +0900 Subject: [PATCH] gpg: Report BEGIN_* status before examining the input. * common/miscellaneous.c (is_openpgp_compressed_packet) (is_file_compressed): Moved to ... * common/iobuf.c: ... in this file. (is_file_compressed): Change the argument to INP, the iobuf. * common/util.h (is_file_compressed): Remove. * common/iobuf.h (is_file_compressed): Add. * g10/cipher-aead.c (write_header): Don't call write_status_printf here. (cipher_filter_aead): Call write_status_printf when called with IOBUFCTRL_INIT. * g10/cipher-cfb.c (write_header): Don't call write_status_printf here. (cipher_filter_cfb): Call write_status_printf when called with IOBUFCTRL_INIT. * g10/encrypt.c (encrypt_simple): Use new is_file_compressed function, after call of iobuf_push_filter. (encrypt_crypt): Likewise. * g10/sign.c (sign_file): Likewise. -- GnuPG-bug-id: 6481 Signed-off-by: NIIBE Yutaka --- common/iobuf.c | 120 +++++++++++++++++++++++++++++++++++++++++ common/iobuf.h | 3 ++ common/miscellaneous.c | 106 ------------------------------------ common/util.h | 2 - g10/cipher-aead.c | 7 ++- g10/cipher-cfb.c | 9 ++-- g10/encrypt.c | 103 ++++++++++++++--------------------- g10/sign.c | 13 +---- 8 files changed, 174 insertions(+), 189 deletions(-) diff --git a/common/iobuf.c b/common/iobuf.c index 62cde27f9..e088812a6 100644 --- a/common/iobuf.c +++ b/common/iobuf.c @@ -3057,3 +3057,123 @@ iobuf_skip_rest (iobuf_t a, unsigned long n, int partial) } } } + + +/* Check whether (BUF,LEN) is valid header for an OpenPGP compressed + * packet. LEN should be at least 6. */ +static int +is_openpgp_compressed_packet (const unsigned char *buf, size_t len) +{ + int c, ctb, pkttype; + int lenbytes; + + ctb = *buf++; len--; + if (!(ctb & 0x80)) + return 0; /* Invalid packet. */ + + if ((ctb & 0x40)) /* New style (OpenPGP) CTB. */ + { + pkttype = (ctb & 0x3f); + if (!len) + return 0; /* Expected first length octet missing. */ + c = *buf++; len--; + if (c < 192) + ; + else if (c < 224) + { + if (!len) + return 0; /* Expected second length octet missing. */ + } + else if (c == 255) + { + if (len < 4) + return 0; /* Expected length octets missing */ + } + } + else /* Old style CTB. */ + { + pkttype = (ctb>>2)&0xf; + lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3)); + if (len < lenbytes) + return 0; /* Not enough length bytes. */ + } + + return (pkttype == 8); +} + + +/* + * Check if the file is compressed, by peeking the iobuf. You need to + * pass the iobuf with INP. Returns true if the buffer seems to be + * compressed. + */ +int +is_file_compressed (iobuf_t inp) +{ + int i; + char buf[32]; + int buflen; + + struct magic_compress_s + { + byte len; + byte extchk; + byte magic[5]; + } magic[] = + { + { 3, 0, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */ + { 3, 0, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */ + { 4, 0, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */ + { 5, 0, { '%', 'P', 'D', 'F', '-'} }, /* PDF */ + { 4, 1, { 0xff, 0xd8, 0xff, 0xe0 } }, /* Maybe JFIF */ + { 5, 2, { 0x89, 'P','N','G', 0x0d} } /* Likely PNG */ + }; + + if (!inp) + return 0; + + for ( ; inp->chain; inp = inp->chain ) + ; + + buflen = iobuf_ioctl (inp, IOBUF_IOCTL_PEEK, sizeof buf, buf); + if (buflen < 0) + { + buflen = 0; + log_debug ("peeking at input failed\n"); + } + + if ( buflen < 6 ) + { + return 0; /* Too short to check - assume uncompressed. */ + } + + for ( i = 0; i < DIM (magic); i++ ) + { + if (!memcmp( buf, magic[i].magic, magic[i].len)) + { + switch (magic[i].extchk) + { + case 0: + return 1; /* Is compressed. */ + case 1: + if (buflen > 11 && !memcmp (buf + 6, "JFIF", 5)) + return 1; /* JFIF: this likely a compressed JPEG. */ + break; + case 2: + if (buflen > 8 + && buf[5] == 0x0a && buf[6] == 0x1a && buf[7] == 0x0a) + return 1; /* This is a PNG. */ + break; + default: + break; + } + } + } + + if (buflen >= 6 && is_openpgp_compressed_packet (buf, buflen)) + { + return 1; /* Already compressed. */ + } + + return 0; /* Not detected as compressed. */ +} diff --git a/common/iobuf.h b/common/iobuf.h index c132c2f3c..ad416fe86 100644 --- a/common/iobuf.h +++ b/common/iobuf.h @@ -629,6 +629,9 @@ void iobuf_set_partial_body_length_mode (iobuf_t a, size_t len); from the following filter (which may or may not return EOF). */ void iobuf_skip_rest (iobuf_t a, unsigned long n, int partial); +/* Check if the file is compressed, by peeking the iobuf. */ +int is_file_compressed (iobuf_t inp); + #define iobuf_where(a) "[don't know]" /* Each time a filter is allocated (via iobuf_alloc()), a diff --git a/common/miscellaneous.c b/common/miscellaneous.c index f19cc539d..1a090b1f5 100644 --- a/common/miscellaneous.c +++ b/common/miscellaneous.c @@ -415,112 +415,6 @@ 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 (const unsigned char *buf, size_t len) -{ - int c, ctb, pkttype; - int lenbytes; - - ctb = *buf++; len--; - if (!(ctb & 0x80)) - return 0; /* Invalid packet. */ - - if ((ctb & 0x40)) /* New style (OpenPGP) CTB. */ - { - pkttype = (ctb & 0x3f); - if (!len) - return 0; /* Expected first length octet missing. */ - c = *buf++; len--; - if (c < 192) - ; - else if (c < 224) - { - if (!len) - return 0; /* Expected second length octet missing. */ - } - else if (c == 255) - { - if (len < 4) - return 0; /* Expected length octets missing */ - } - } - else /* Old style CTB. */ - { - pkttype = (ctb>>2)&0xf; - lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3)); - if (len < lenbytes) - return 0; /* Not enough length bytes. */ - } - - return (pkttype == 8); -} - - - -/* - * 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 byte *buf, unsigned int buflen) -{ - int i; - - struct magic_compress_s - { - byte len; - byte extchk; - byte magic[5]; - } magic[] = - { - { 3, 0, { 0x42, 0x5a, 0x68, 0x00 } }, /* bzip2 */ - { 3, 0, { 0x1f, 0x8b, 0x08, 0x00 } }, /* gzip */ - { 4, 0, { 0x50, 0x4b, 0x03, 0x04 } }, /* (pk)zip */ - { 5, 0, { '%', 'P', 'D', 'F', '-'} }, /* PDF */ - { 4, 1, { 0xff, 0xd8, 0xff, 0xe0 } }, /* Maybe JFIF */ - { 5, 2, { 0x89, 'P','N','G', 0x0d} } /* Likely PNG */ - }; - - if ( buflen < 6 ) - { - return 0; /* Too short to check - assume uncompressed. */ - } - - for ( i = 0; i < DIM (magic); i++ ) - { - if (!memcmp( buf, magic[i].magic, magic[i].len)) - { - switch (magic[i].extchk) - { - case 0: - return 1; /* Is compressed. */ - case 1: - if (buflen > 11 && !memcmp (buf + 6, "JFIF", 5)) - return 1; /* JFIF: this likely a compressed JPEG. */ - break; - case 2: - if (buflen > 8 - && buf[5] == 0x0a && buf[6] == 0x1a && buf[7] == 0x0a) - return 1; /* This is a PNG. */ - break; - default: - break; - } - } - } - - if (buflen >= 6 && is_openpgp_compressed_packet (buf, buflen)) - { - return 1; /* Already compressed. */ - } - - return 0; /* Not detected as compressed. */ -} - - /* Try match against each substring of multistr, delimited by | */ int match_multistr (const char *multistr,const char *match) diff --git a/common/util.h b/common/util.h index aa24e39e6..6b948510e 100644 --- a/common/util.h +++ b/common/util.h @@ -360,8 +360,6 @@ 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 byte *buf, unsigned int buflen); - int match_multistr (const char *multistr,const char *match); int gnupg_compare_version (const char *a, const char *b); diff --git a/g10/cipher-aead.c b/g10/cipher-aead.c index 640d8432f..0c07e65de 100644 --- a/g10/cipher-aead.c +++ b/g10/cipher-aead.c @@ -174,8 +174,6 @@ write_header (cipher_filter_context_t *cfx, iobuf_t a) log_debug ("aead packet: len=%lu extralen=%d\n", (unsigned long)ed.len, ed.extralen); - write_status_printf (STATUS_BEGIN_ENCRYPTION, "0 %d %d", - cfx->dek->algo, ed.aead_algo); print_cipher_algo_note (cfx->dek->algo); if (build_packet( a, &pkt)) @@ -488,6 +486,11 @@ cipher_filter_aead (void *opaque, int control, { mem2str (buf, "cipher_filter_aead", *ret_len); } + else if (control == IOBUFCTRL_INIT) + { + write_status_printf (STATUS_BEGIN_ENCRYPTION, "0 %d %d", + cfx->dek->algo, cfx->dek->use_aead); + } return rc; } diff --git a/g10/cipher-cfb.c b/g10/cipher-cfb.c index 3ba8eb738..29bf2477c 100644 --- a/g10/cipher-cfb.c +++ b/g10/cipher-cfb.c @@ -72,9 +72,6 @@ write_header (cipher_filter_context_t *cfx, iobuf_t a) log_info (_("Hint: Do not use option %s\n"), "--rfc2440"); } - write_status_printf (STATUS_BEGIN_ENCRYPTION, "%d %d", - ed.mdc_method, cfx->dek->algo); - init_packet (&pkt); pkt.pkttype = cfx->dek->use_mdc? PKT_ENCRYPTED_MDC : PKT_ENCRYPTED; pkt.pkt.encrypted = &ed; @@ -182,6 +179,12 @@ cipher_filter_cfb (void *opaque, int control, { mem2str (buf, "cipher_filter_cfb", *ret_len); } + else if (control == IOBUFCTRL_INIT) + { + write_status_printf (STATUS_BEGIN_ENCRYPTION, "%d %d", + cfx->dek->use_mdc ? DIGEST_ALGO_SHA1 : 0, + cfx->dek->algo); + } return rc; } diff --git a/g10/encrypt.c b/g10/encrypt.c index 687b4344e..a524326bb 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -410,8 +410,6 @@ 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)) { @@ -448,14 +446,6 @@ 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) @@ -517,17 +507,6 @@ encrypt_simple (const char *filename, int mode, int use_seskey) /**/ : "CFB"); } - if (do_compress - && cfx.dek - && (cfx.dek->use_mdc || cfx.dek->use_aead) - && !opt.explicit_compress_option - && is_file_compressed (peekbuf, peekbuflen)) - { - if (opt.verbose) - log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]"); - do_compress = 0; - } - if ( rc || (rc = open_outfile (-1, filename, opt.armor? 1:0, 0, &out ))) { iobuf_cancel (inp); @@ -598,6 +577,24 @@ encrypt_simple (const char *filename, int mode, int use_seskey) else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ + /* Register the cipher filter. */ + if (mode) + iobuf_push_filter (out, + cfx.dek->use_aead? cipher_filter_aead + /**/ : cipher_filter_cfb, + &cfx ); + + if (do_compress + && cfx.dek + && (cfx.dek->use_mdc || cfx.dek->use_aead) + && !opt.explicit_compress_option + && is_file_compressed (inp)) + { + if (opt.verbose) + log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]"); + do_compress = 0; + } + if (!opt.no_literal) { /* Note that PT has been initialized above in !no_literal mode. */ @@ -617,13 +614,6 @@ encrypt_simple (const char *filename, int mode, int use_seskey) pkt.pkt.generic = NULL; } - /* Register the cipher filter. */ - if (mode) - iobuf_push_filter (out, - cfx.dek->use_aead? cipher_filter_aead - /**/ : cipher_filter_cfb, - &cfx ); - /* Register the compress filter. */ if ( do_compress ) { @@ -783,7 +773,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, PKT_plaintext *pt = NULL; DEK *symkey_dek = NULL; STRING2KEY *symkey_s2k = NULL; - int rc = 0, rc2 = 0; + int rc = 0; u32 filesize; cipher_filter_context_t cfx; armor_filter_context_t *afx = NULL; @@ -792,8 +782,6 @@ 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. */ @@ -866,14 +854,6 @@ 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) @@ -900,25 +880,6 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, if (!cfx.dek->use_aead) cfx.dek->use_mdc = !!use_mdc (pk_list, cfx.dek->algo); - /* Only do the is-file-already-compressed check if we are using a - * MDC or AEAD. This forces compressed files to be re-compressed if - * we do not have a MDC to give some protection against chosen - * ciphertext attacks. */ - if (do_compress - && (cfx.dek->use_mdc || cfx.dek->use_aead) - && !opt.explicit_compress_option - && is_file_compressed (peekbuf, peekbuflen)) - { - if (opt.verbose) - log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]"); - do_compress = 0; - } - if (rc2) - { - rc = rc2; - goto leave; - } - make_session_key (cfx.dek); if (DBG_CRYPTO) log_printhex (cfx.dek->key, cfx.dek->keylen, "DEK is: "); @@ -960,6 +921,26 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, else filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */ + /* Register the cipher filter. */ + iobuf_push_filter (out, + cfx.dek->use_aead? cipher_filter_aead + /**/ : cipher_filter_cfb, + &cfx); + + /* Only do the is-file-already-compressed check if we are using a + * MDC or AEAD. This forces compressed files to be re-compressed if + * we do not have a MDC to give some protection against chosen + * ciphertext attacks. */ + if (do_compress + && (cfx.dek->use_mdc || cfx.dek->use_aead) + && !opt.explicit_compress_option + && is_file_compressed (inp)) + { + if (opt.verbose) + log_info(_("'%s' already compressed\n"), filename? filename: "[stdin]"); + do_compress = 0; + } + if (!opt.no_literal) { pt->timestamp = make_timestamp(); @@ -974,12 +955,6 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, else cfx.datalen = filesize && !do_compress ? filesize : 0; - /* Register the cipher filter. */ - iobuf_push_filter (out, - cfx.dek->use_aead? cipher_filter_aead - /**/ : cipher_filter_cfb, - &cfx); - /* Register the compress filter. */ if (do_compress) { diff --git a/g10/sign.c b/g10/sign.c index b5e9d422d..fcb1bb749 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -1035,9 +1035,6 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, int multifile = 0; u32 duration=0; pt_extra_hash_data_t extrahash = NULL; - char peekbuf[32]; - int peekbuflen = 0; - pfx = new_progress_context (); afx = new_armor_context (); @@ -1096,14 +1093,6 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, 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); } @@ -1261,7 +1250,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, int compr_algo = opt.compress_algo; if (!opt.explicit_compress_option - && is_file_compressed (peekbuf, peekbuflen)) + && is_file_compressed (inp)) { if (opt.verbose) log_info(_("'%s' already compressed\n"), fname? fname: "[stdin]");