From ec409e62aea6cc829299be794f9d035d033cb51b Mon Sep 17 00:00:00 2001 From: "Neal H. Walfield" Date: Thu, 5 Nov 2015 12:20:42 +0100 Subject: [PATCH] gpg: Add support for unwrapping the outer level of encryption. * g10/decrypt-data.c (decrypt_data): If OPT.UNWRAP_ENCRYPTION is set, copy the data to the output file instead of continuing to process it. * g10/gpg.c (enum cmd_and_opt_values): Add new value oUnwrap. (opts): Handle oUnwrap. (main): Likewise. * g10/options.h (opt): Add field unwrap_encryption. * g10/plaintext.c (handle_plaintext): Break the output file selection functionality into ... (get_output_file): ... this new function. -- Signed-off-by: Neal H. Walfield GnuPG-bug-id: 1060 Debian-bug-id: 282061 --- g10/decrypt-data.c | 33 ++++++++++- g10/gpg.c | 5 ++ g10/options.h | 2 + g10/packet.h | 2 + g10/plaintext.c | 136 +++++++++++++++++++++++++++++---------------- 5 files changed, 130 insertions(+), 48 deletions(-) diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c index 570a71d11..b575f39dd 100644 --- a/g10/decrypt-data.c +++ b/g10/decrypt-data.c @@ -221,7 +221,38 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) else iobuf_push_filter ( ed->buf, decode_filter, dfx ); - proc_packets (ctrl, procctx, ed->buf ); + if (opt.unwrap_encryption) + { + char *filename; + estream_t fp; + rc = get_output_file ("", 0, ed->buf, &filename, &fp); + if (! rc) + { + iobuf_t output = iobuf_esopen (fp, "w", 0); + armor_filter_context_t *afx = NULL; + + if (opt.armor) + { + afx = new_armor_context (); + push_armor_filter (afx, output); + } + + iobuf_copy (output, ed->buf); + if ((rc = iobuf_error (ed->buf))) + log_error (_("error reading: %s\n"), + filename, gpg_strerror (rc)); + else if ((rc = iobuf_error (output))) + log_error (_("error writing output ('%s'): %s\n"), + filename, gpg_strerror (rc)); + + iobuf_close (output); + if (afx) + release_armor_context (afx); + } + } + else + proc_packets (ctrl, procctx, ed->buf ); + ed->buf = NULL; if (dfx->eof_seen > 1 ) rc = gpg_error (GPG_ERR_INV_PACKET); diff --git a/g10/gpg.c b/g10/gpg.c index cdf7b0d7f..ef283b487 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -391,6 +391,7 @@ enum cmd_and_opt_values oTOFUDefaultPolicy, oTOFUDBFormat, oWeakDigest, + oUnwrap, oNoop }; @@ -753,6 +754,7 @@ static ARGPARSE_OPTS opts[] = { "personal-compress-preferences", "@"), ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"), ARGPARSE_s_s (oWeakDigest, "weak-digest","@"), + ARGPARSE_s_n (oUnwrap, "unwrap", "@"), /* Aliases. I constantly mistype these, and assume other people do as well. */ @@ -3147,6 +3149,9 @@ main (int argc, char **argv) case oWeakDigest: additional_weak_digest(pargs.r.ret_str); break; + case oUnwrap: + opt.unwrap_encryption = 1; + break; case oDisplay: set_opt_session_env ("DISPLAY", pargs.r.ret_str); diff --git a/g10/options.h b/g10/options.h index bc92dd746..c8541b2e4 100644 --- a/g10/options.h +++ b/g10/options.h @@ -262,6 +262,8 @@ struct int passphrase_repeat; int pinentry_mode; + + int unwrap_encryption; } opt; /* CTRL is used to keep some global variables we currently can't diff --git a/g10/packet.h b/g10/packet.h index 21c80f3f2..387a5b5cd 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -663,6 +663,8 @@ int handle_compressed (ctrl_t ctrl, void *ctx, PKT_compressed *cd, int decrypt_data (ctrl_t ctrl, void *ctx, PKT_encrypted *ed, DEK *dek ); /*-- plaintext.c --*/ +gpg_error_t get_output_file (const byte *embedded_name, int embedded_namelen, + iobuf_t data, char **fnamep, estream_t *fpp); int handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx, int nooutput, int clearsig ); int ask_for_detached_datafile( gcry_md_hd_t md, gcry_md_hd_t md2, diff --git a/g10/plaintext.c b/g10/plaintext.c index 7929f6643..aeee2ac49 100644 --- a/g10/plaintext.c +++ b/g10/plaintext.c @@ -40,53 +40,29 @@ #include "i18n.h" -/* Handle a plaintext packet. If MFX is not NULL, update the MDs - * Note: We should have used the filter stuff here, but we have to add - * some easy mimic to set a read limit, so we calculate only the bytes - * from the plaintext. */ -int -handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx, - int nooutput, int clearsig) +/* Get the output filename. On success, the actual filename that is + used is set in *FNAMEP and a filepointer is returned in *FP. + + EMBEDDED_NAME AND EMBEDDED_NAMELEN are normally stored in a + plaintext packet. EMBEDDED_NAMELEN should not include any NUL + terminator (EMBEDDED_NAME does not need to be NUL terminated). + + DATA is the iobuf containing the input data. We just use it to get + the input file's filename. + + On success, the caller is responsible for calling xfree on *FNAMEP + and calling es_close on *FPP. */ +gpg_error_t +get_output_file (const byte *embedded_name, int embedded_namelen, + iobuf_t data, char **fnamep, estream_t *fpp) { + gpg_error_t err = 0; char *fname = NULL; estream_t fp = NULL; - static off_t count = 0; - int err = 0; - int c; - int convert = (pt->mode == 't' || pt->mode == 'u'); -#ifdef __riscos__ - int filetype = 0xfff; -#endif - - /* Let people know what the plaintext info is. This allows the - receiving program to try and do something different based on the - format code (say, recode UTF-8 to local). */ - if (!nooutput && is_status_enabled ()) - { - char status[50]; - - /* Better make sure that stdout has been flushed in case the - output will be written to it. This is to make sure that no - not-yet-flushed stuff will be written after the plaintext - status message. */ - es_fflush (es_stdout); - - snprintf (status, sizeof status, - "%X %lu ", (byte) pt->mode, (ulong) pt->timestamp); - write_status_text_and_buffer (STATUS_PLAINTEXT, - status, pt->name, pt->namelen, 0); - - if (!pt->is_partial) - { - snprintf (status, sizeof status, "%lu", (ulong) pt->len); - write_status_text (STATUS_PLAINTEXT_LENGTH, status); - } - } + int nooutput = 0; /* Create the filename as C string. */ - if (nooutput) - ; - else if (opt.outfp) + if (opt.outfp) { fname = xtrystrdup ("[FP]"); if (!fname) @@ -104,16 +80,17 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx, goto leave; } } - else if (pt->namelen == 8 && !memcmp (pt->name, "_CONSOLE", 8)) + else if (embedded_namelen == 8 && !memcmp (embedded_name, "_CONSOLE", 8)) { log_info (_("data not saved; use option \"--output\" to save it\n")); nooutput = 1; } else if (!opt.flags.use_embedded_filename) { - fname = make_outfile_name (iobuf_get_real_fname (pt->buf)); + if (data) + fname = make_outfile_name (iobuf_get_real_fname (data)); if (!fname) - fname = ask_outfile_name (pt->name, pt->namelen); + fname = ask_outfile_name (embedded_name, embedded_namelen); if (!fname) { err = gpg_error (GPG_ERR_GENERAL); /* Can't create file. */ @@ -121,7 +98,7 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx, } } else - fname = utf8_to_native (pt->name, pt->namelen, 0); + fname = utf8_to_native (embedded_name, embedded_namelen, 0); if (nooutput) ; @@ -205,7 +182,8 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx, /* If there's a ,xxx extension in the embedded filename, use that, else check whether the user input (in fname) has a ,xxx appended, then use that in preference */ - if ((c = riscos_get_filetype_from_string (pt->name, pt->namelen)) != -1) + if ((c = riscos_get_filetype_from_string (embedded_name, + embedded_namelen)) != -1) filetype = c; if ((c = riscos_get_filetype_from_string (fname, strlen (fname))) != -1) filetype = c; @@ -213,6 +191,70 @@ handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx, } #endif /* __riscos__ */ + leave: + if (err) + { + if (fp && fp != es_stdout && fp != opt.outfp) + es_fclose (fp); + xfree (fname); + return err; + } + + *fnamep = fname; + *fpp = fp; + return 0; +} + +/* Handle a plaintext packet. If MFX is not NULL, update the MDs + * Note: We should have used the filter stuff here, but we have to add + * some easy mimic to set a read limit, so we calculate only the bytes + * from the plaintext. */ +int +handle_plaintext (PKT_plaintext * pt, md_filter_context_t * mfx, + int nooutput, int clearsig) +{ + char *fname = NULL; + estream_t fp = NULL; + static off_t count = 0; + int err = 0; + int c; + int convert = (pt->mode == 't' || pt->mode == 'u'); +#ifdef __riscos__ + int filetype = 0xfff; +#endif + + /* Let people know what the plaintext info is. This allows the + receiving program to try and do something different based on the + format code (say, recode UTF-8 to local). */ + if (!nooutput && is_status_enabled ()) + { + char status[50]; + + /* Better make sure that stdout has been flushed in case the + output will be written to it. This is to make sure that no + not-yet-flushed stuff will be written after the plaintext + status message. */ + es_fflush (es_stdout); + + snprintf (status, sizeof status, + "%X %lu ", (byte) pt->mode, (ulong) pt->timestamp); + write_status_text_and_buffer (STATUS_PLAINTEXT, + status, pt->name, pt->namelen, 0); + + if (!pt->is_partial) + { + snprintf (status, sizeof status, "%lu", (ulong) pt->len); + write_status_text (STATUS_PLAINTEXT_LENGTH, status); + } + } + + if (! nooutput) + { + err = get_output_file (pt->name, pt->namelen, pt->buf, &fname, &fp); + if (err) + goto leave; + } + if (!pt->is_partial) { /* We have an actual length (which might be zero). */