1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-11-10 21:38:50 +01:00

gpg: New commands --add-recipients and --change-recipients.

* g10/gpg.c (aAddRecipients, aChangeRecipients): New consts.
(opts): Add --add-recipients and --change-recipients.
(main): Handle them.
* g10/gpg.h (struct server_control_s): Add fields modify_recipients,
clear_recipients, and last_read_ctb.
* g10/armor.c (was_armored): New.
* g10/decrypt.c (decrypt_message): Add optional arg 'remusr'.  Handle
re-encryption if desired.
* g10/encrypt.c (write_pubkey_enc): Factor info printing out to ...
(show_encrypted_for_user_info): new.
(reencrypt_to_new_recipients): New.
* g10/packet.h (struct parse_packet_ctx_s): Add fields only_fookey_enc
and last_ctb.
(init_parse_packet): Clear them.
* g10/parse-packet.c (parse): Store CTB in the context.  Early return
on pubkey_enc and symkey_enc packets if requested.
* g10/mainproc.c (proc_encrypted): Allow for PKT being NULL.  Return
early in modify-recipients mode.
(proc_encryption_packets): Add two optional args 'r_dek' and 'r_list'.
Adjust callers.  Call do_proc_packets in modify-recipients mode
depending on the optional args.
(do_proc_packets): Add arg 'keep_dek_and_list'.  Adjust callers.  Save
the last read CTB in CTRL and return after the last fooenc_enc
packets.
--

This basically works but does not yet handle symmetric encrypted
packets (symkey_enc).

GnuPG-bug-id: 1825
(Yes, this is an at least 9 year old feature request)
This commit is contained in:
Werner Koch 2024-09-09 16:41:35 +02:00
parent 2cc340eca0
commit d528d0b065
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
11 changed files with 332 additions and 67 deletions

View File

@ -162,6 +162,14 @@ push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf)
} }
/* This function returns true if the armor filter detected that the
* input was indeed armored. Gives a valid result only after the
* first PGP packet has been read. */
int
was_armored (armor_filter_context_t *afx)
{
return (afx && !afx->inp_bypass);
}

View File

@ -1,6 +1,7 @@
/* decrypt.c - decrypt and verify data /* decrypt.c - decrypt and verify data
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
* 2007, 2009 Free Software Foundation, Inc. * 2007, 2009 Free Software Foundation, Inc.
* Copyright (C) 2024 g10 Code GmbH
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -16,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>
@ -35,19 +37,26 @@
#include "../common/status.h" #include "../common/status.h"
#include "../common/i18n.h" #include "../common/i18n.h"
/* Assume that the input is an encrypted message and decrypt /* Assume that the input is an encrypted message and decrypt
* (and if signed, verify the signature on) it. * (and if signed, verify the signature on) it.
* This command differs from the default operation, as it never * This command differs from the default operation, as it never
* writes to the filename which is included in the file and it * writes to the filename which is included in the file and it
* rejects files which don't begin with an encrypted message. * rejects files which don't begin with an encrypted message.
*
* REMUSR is only used in the modify_recipients mode and speicifies
* the additional or new recipients to use.
*/ */
int gpg_error_t
decrypt_message (ctrl_t ctrl, const char *filename) decrypt_message (ctrl_t ctrl, const char *filename, strlist_t remusr)
{ {
IOBUF fp; gpg_error_t err;
iobuf_t fp;
armor_filter_context_t *afx = NULL; armor_filter_context_t *afx = NULL;
progress_filter_context_t *pfx; progress_filter_context_t *pfx;
int rc; DEK *dek = NULL;
struct pubkey_enc_list *pkenc_list = NULL;
pfx = new_progress_context (); pfx = new_progress_context ();
@ -61,14 +70,18 @@ decrypt_message (ctrl_t ctrl, const char *filename)
} }
if ( !fp ) if ( !fp )
{ {
rc = gpg_error_from_syserror (); err = gpg_error_from_syserror ();
log_error (_("can't open '%s': %s\n"), print_fname_stdin(filename), log_error (_("can't open '%s': %s\n"), print_fname_stdin(filename),
gpg_strerror (rc)); gpg_strerror (err));
release_progress_context (pfx); release_progress_context (pfx);
return rc; return err;
} }
handle_progress (pfx, fp, filename); /* Push the progress filter unless we are in add recipient mode.
* The latter may also work but for now we better avoid any possible
* complications. */
if (!ctrl->modify_recipients)
handle_progress (pfx, fp, filename);
if ( !opt.no_armor ) if ( !opt.no_armor )
{ {
@ -86,19 +99,44 @@ decrypt_message (ctrl_t ctrl, const char *filename)
} }
else else
opt.flags.dummy_outfile = 0; opt.flags.dummy_outfile = 0;
rc = proc_encryption_packets (ctrl, NULL, fp ); if (!ctrl->modify_recipients)
err = proc_encryption_packets (ctrl, NULL, fp, NULL, NULL);
else
err = proc_encryption_packets (ctrl, NULL, fp, &dek, &pkenc_list);
if (opt.flags.dummy_outfile) if (opt.flags.dummy_outfile)
opt.outfile = NULL; opt.outfile = NULL;
if (ctrl->modify_recipients && (err || !dek) )
log_error (_("modifiying the recipients is not possible: %s\n"),
err? gpg_strerror (err) : _("decryption failed"));
else if (ctrl->modify_recipients)
{
/* We apply an armor to the output if --armor was used or if the
* input was already armored and --no-armor was not given. */
int armor = opt.armor || (was_armored (afx) && !opt.no_armor);
err = reencrypt_to_new_recipients (ctrl, armor, filename, fp,
remusr, dek, pkenc_list);
}
xfree (dek);
while (pkenc_list)
{
struct pubkey_enc_list *tmp = pkenc_list->next;
release_pubkey_enc_parts (&pkenc_list->d);
xfree (pkenc_list);
pkenc_list = tmp;
}
iobuf_close (fp); iobuf_close (fp);
release_armor_context (afx); release_armor_context (afx);
release_progress_context (pfx); release_progress_context (pfx);
return rc; return err;
} }
/* Same as decrypt_message but takes a file descriptor for input and /* Same as decrypt_message but takes a file descriptor for input and
output. */ output. Only used by the unfinished server mode. */
gpg_error_t gpg_error_t
decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd, decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd,
gnupg_fd_t output_fd) gnupg_fd_t output_fd)
@ -173,7 +211,7 @@ decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd,
} }
} }
err = proc_encryption_packets (ctrl, NULL, fp ); err = proc_encryption_packets (ctrl, NULL, fp, NULL, NULL);
iobuf_close (fp); iobuf_close (fp);
es_fclose (opt.outfp); es_fclose (opt.outfp);

View File

@ -46,6 +46,30 @@ static int encrypt_simple( const char *filename, int mode, int use_seskey );
static int write_pubkey_enc_from_list (ctrl_t ctrl, static int write_pubkey_enc_from_list (ctrl_t ctrl,
PK_LIST pk_list, DEK *dek, iobuf_t out); PK_LIST pk_list, DEK *dek, iobuf_t out);
/* Helper for show the "encrypted for USER" during encryption.
* PUBKEY_USAGE is used to figure out whether this is an ADSK key. */
static void
show_encrypted_for_user_info (ctrl_t ctrl, unsigned int pubkey_usage,
PKT_pubkey_enc *enc, DEK *dek)
{
char *ustr = get_user_id_string_native (ctrl, enc->keyid);
if ((pubkey_usage & PUBKEY_USAGE_RENC))
{
char *tmpustr = xstrconcat (ustr, " [ADSK]", NULL);
xfree (ustr);
ustr = tmpustr;
}
log_info (_("%s/%s.%s encrypted for: \"%s\"\n"),
openpgp_pk_algo_name (enc->pubkey_algo),
openpgp_cipher_algo_name (dek->algo),
dek->use_aead? openpgp_aead_algo_name (dek->use_aead)
/**/ : "CFB",
ustr );
xfree (ustr);
}
/**************** /****************
* Encrypt FILENAME with only the symmetric cipher. Take input from * Encrypt FILENAME with only the symmetric cipher. Take input from
* stdin if FILENAME is NULL. If --force-aead is used we use an SKESK. * stdin if FILENAME is NULL. If --force-aead is used we use an SKESK.
@ -389,10 +413,11 @@ use_mdc (pk_list_t pk_list,int algo)
} }
/* We don't want to use use_seskey yet because older gnupg versions /* This function handles the --symmetric only (MODE true) and --store
can't handle it, and there isn't really any point unless we're * (MODE false) cases. We don't want to use USE_SESKEY by default
making a message that can be decrypted by a public key or * very old gnupg versions can't handle it, and there isn't really any
passphrase. */ * point unless we're making a message that can be decrypted by a
* public key or passphrase. */
static int static int
encrypt_simple (const char *filename, int mode, int use_seskey) encrypt_simple (const char *filename, int mode, int use_seskey)
{ {
@ -1034,6 +1059,90 @@ encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename,
} }
/* Re-encrypt files with a set of new recipients. Note that this
* function is called by decrypt_message. INFP is the iobuf from the
* input file which is positioned right after the pubkey_enc and
* symkey_enc packets. */
gpg_error_t
reencrypt_to_new_recipients (ctrl_t ctrl, int armor, const char *filename,
iobuf_t infp, strlist_t recipients,
DEK *dek, struct pubkey_enc_list *pkenc_list)
{
gpg_error_t err;
int save_no_encrypt_to;
pk_list_t newpk_list = NULL;
iobuf_t outfp = NULL;
armor_filter_context_t *outafx = NULL;
PACKET pkt;
struct pubkey_enc_list *el;
unsigned int count;
/* Get the keys for all additional recipients but do not encrypt to
* the encrypt-to keys. */
save_no_encrypt_to = opt.no_encrypt_to;
opt.no_encrypt_to = 1;
err = build_pk_list (ctrl, recipients, &newpk_list);
opt.no_encrypt_to = save_no_encrypt_to;
if (err)
goto leave;
/* Note that we use by default the suffixes .gpg or .asc */
err = open_outfile (GNUPG_INVALID_FD, filename, armor? 1:0, 0, &outfp);
if (err)
goto leave;
if (armor)
{
outafx = new_armor_context ();
push_armor_filter (outafx, outfp);
}
/* Write the new recipients first. */
err = write_pubkey_enc_from_list (ctrl, newpk_list, dek, outfp);
if (err)
goto leave;
/* The write the old recipients in --add-recipients mode. */
for (count=0, el = pkenc_list; el; el = el->next, count++)
if (!ctrl->clear_recipients)
{
if (opt.verbose)
show_encrypted_for_user_info (ctrl, 0, &el->d, dek);
init_packet (&pkt);
pkt.pkttype = PKT_PUBKEY_ENC;
pkt.pkt.pubkey_enc = &el->d;
err = build_packet (outfp, &pkt);
if (err)
log_error ("build_packet(pubkey_enc) failed: %s\n",
gpg_strerror (err));
}
if (ctrl->clear_recipients && opt.verbose)
log_info (_("number of removed recipients: %u\n"), count);
iobuf_put (outfp, ctrl->last_read_ctb);
/* Finally copy the bulk of the message. */
iobuf_copy (outfp, infp);
if ((err = iobuf_error (infp)))
log_error (_("error reading '%s': %s\n"),
iobuf_get_fname_nonnull (infp), gpg_strerror (err));
else if ((err = iobuf_error (outfp)))
log_error (_("error writing '%s': %s\n"),
iobuf_get_fname_nonnull (outfp), gpg_strerror (err));
leave:
if (err)
iobuf_cancel (outfp);
else
iobuf_close (outfp);
release_armor_context (outafx);
release_pk_list (newpk_list);
return err;
}
/* /*
* Filter to do a complete public key encryption. * Filter to do a complete public key encryption.
*/ */
@ -1144,22 +1253,7 @@ write_pubkey_enc (ctrl_t ctrl,
else else
{ {
if ( opt.verbose ) if ( opt.verbose )
{ show_encrypted_for_user_info (ctrl, pk->pubkey_usage, enc, dek);
char *ustr = get_user_id_string_native (ctrl, enc->keyid);
if ((pk->pubkey_usage & PUBKEY_USAGE_RENC))
{
char *tmpustr = xstrconcat (ustr, " [ADSK]", NULL);
xfree (ustr);
ustr = tmpustr;
}
log_info (_("%s/%s.%s encrypted for: \"%s\"\n"),
openpgp_pk_algo_name (enc->pubkey_algo),
openpgp_cipher_algo_name (dek->algo),
dek->use_aead? openpgp_aead_algo_name (dek->use_aead)
/**/ : "CFB",
ustr );
xfree (ustr);
}
/* And write it. */ /* And write it. */
init_packet (&pkt); init_packet (&pkt);
pkt.pkttype = PKT_PUBKEY_ENC; pkt.pkttype = PKT_PUBKEY_ENC;

View File

@ -175,6 +175,7 @@ void free_md_filter_context( md_filter_context_t *mfx );
armor_filter_context_t *new_armor_context (void); armor_filter_context_t *new_armor_context (void);
void release_armor_context (armor_filter_context_t *afx); void release_armor_context (armor_filter_context_t *afx);
int push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf); int push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf);
int was_armored (armor_filter_context_t *afx);
int use_armor_filter( iobuf_t a ); int use_armor_filter( iobuf_t a );
/*-- compress.c --*/ /*-- compress.c --*/

View File

@ -115,6 +115,8 @@ enum cmd_and_opt_values
oKnownNotation, oKnownNotation,
aEncrFiles, aEncrFiles,
aEncrSym, aEncrSym,
aAddRecipients,
aChangeRecipients,
aDecryptFiles, aDecryptFiles,
aClearsign, aClearsign,
aStore, aStore,
@ -481,6 +483,8 @@ static gpgrt_opt_t opts[] = {
ARGPARSE_c (aDecryptFiles, "decrypt-files", "@"), ARGPARSE_c (aDecryptFiles, "decrypt-files", "@"),
ARGPARSE_c (aVerify, "verify" , N_("verify a signature")), ARGPARSE_c (aVerify, "verify" , N_("verify a signature")),
ARGPARSE_c (aVerifyFiles, "verify-files" , "@" ), ARGPARSE_c (aVerifyFiles, "verify-files" , "@" ),
ARGPARSE_c (aAddRecipients, "add-recipients", "@" ),
ARGPARSE_c (aChangeRecipients, "change-recipients", "@" ),
ARGPARSE_c (aListKeys, "list-keys", N_("list keys")), ARGPARSE_c (aListKeys, "list-keys", N_("list keys")),
ARGPARSE_c (aListKeys, "list-public-keys", "@" ), ARGPARSE_c (aListKeys, "list-public-keys", "@" ),
ARGPARSE_c (aListSigs, "list-signatures", N_("list keys and signatures")), ARGPARSE_c (aListSigs, "list-signatures", N_("list keys and signatures")),
@ -2739,6 +2743,8 @@ main (int argc, char **argv)
case aExportOwnerTrust: case aExportOwnerTrust:
case aImportOwnerTrust: case aImportOwnerTrust:
case aRebuildKeydbCaches: case aRebuildKeydbCaches:
case aAddRecipients:
case aChangeRecipients:
set_cmd (&cmd, pargs.r_opt); set_cmd (&cmd, pargs.r_opt);
break; break;
@ -4168,6 +4174,12 @@ main (int argc, char **argv)
case aStore: case aStore:
cmdname="--store"; cmdname="--store";
break; break;
case aAddRecipients:
cmdname="--add-recipients";
break;
case aChangeRecipients:
cmdname="--change-recipients";
break;
default: default:
cmdname=NULL; cmdname=NULL;
break; break;
@ -4262,7 +4274,9 @@ main (int argc, char **argv)
|| cmd == aEncrSym || cmd == aEncrSym
|| cmd == aSym || cmd == aSym
|| cmd == aSignSym || cmd == aSignSym
|| cmd == aSignEncrSym, || cmd == aSignEncrSym
|| cmd == aAddRecipients
|| cmd == aChangeRecipients,
opt.def_cipher_algo, opt.def_cipher_algo,
GCRY_CIPHER_MODE_NONE)) GCRY_CIPHER_MODE_NONE))
log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), log_error (_("cipher algorithm '%s' may not be used in %s mode\n"),
@ -4509,6 +4523,26 @@ main (int argc, char **argv)
} }
break; break;
case aChangeRecipients: /* Change recipients of the encrypted file. */
ctrl->clear_recipients = 1;
/* fallthru */
case aAddRecipients: /* Add recipients to the encrypted file. */
ctrl->modify_recipients = 1;
if (argc > 1)
{
if (cmd == aAddRecipients)
wrong_args("--add-recipients [filename]");
else
wrong_args("--change-recipients [filename]");
}
if ((rc = decrypt_message (ctrl, fname, remusr)))
{
write_status_failure ("modify-recipients", rc);
log_error ("%s: modify recipients failed: %s\n",
print_fname_stdin (fname), gpg_strerror (rc));
}
break;
case aEncrSym: case aEncrSym:
/* This works with PGP 8 in the sense that it acts just like a /* This works with PGP 8 in the sense that it acts just like a
symmetric message. It doesn't work at all with 2 or 6. It symmetric message. It doesn't work at all with 2 or 6. It
@ -4650,7 +4684,7 @@ main (int argc, char **argv)
{ {
if( argc > 1 ) if( argc > 1 )
wrong_args("--decrypt [filename]"); wrong_args("--decrypt [filename]");
if( (rc = decrypt_message (ctrl, fname) )) if( (rc = decrypt_message (ctrl, fname, NULL) ))
{ {
write_status_failure ("decrypt", rc); write_status_failure ("decrypt", rc);
log_error("decrypt_message failed: %s\n", gpg_strerror (rc) ); log_error("decrypt_message failed: %s\n", gpg_strerror (rc) );

View File

@ -122,6 +122,15 @@ struct server_control_s
unsigned char *secret_keygrips; unsigned char *secret_keygrips;
size_t secret_keygrips_len; size_t secret_keygrips_len;
int no_more_secret_keygrips; int no_more_secret_keygrips;
/* This first flag is set to true if we are running a
* --add-recipients or --change-recipients command. The second if
* it is --change-recipients. */
unsigned int modify_recipients:1;
unsigned int clear_recipients:1;
/* Temporary used to pass the last read byte up the call chain. */
int last_read_ctb;
}; };

View File

@ -248,6 +248,11 @@ int encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename,
gnupg_fd_t outputfd); gnupg_fd_t outputfd);
void encrypt_crypt_files (ctrl_t ctrl, void encrypt_crypt_files (ctrl_t ctrl,
int nfiles, char **files, strlist_t remusr); int nfiles, char **files, strlist_t remusr);
gpg_error_t reencrypt_to_new_recipients (ctrl_t ctrl, int armor,
const char *filename, iobuf_t infp,
strlist_t recipients,
DEK *dek,
struct pubkey_enc_list *pkenc_list);
int encrypt_filter (void *opaque, int control, int encrypt_filter (void *opaque, int control,
iobuf_t a, byte *buf, size_t *ret_len); iobuf_t a, byte *buf, size_t *ret_len);
@ -501,7 +506,8 @@ void check_assert_signer_list (const char *mainpkhex, const char *pkhex);
void check_assert_pubkey_algo (const char *algostr, const char *pkhex); void check_assert_pubkey_algo (const char *algostr, const char *pkhex);
/*-- decrypt.c --*/ /*-- decrypt.c --*/
int decrypt_message (ctrl_t ctrl, const char *filename ); gpg_error_t decrypt_message (ctrl_t ctrl, const char *filename,
strlist_t remusr);
gpg_error_t decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd, gpg_error_t decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_fd,
gnupg_fd_t output_fd); gnupg_fd_t output_fd);
void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]); void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]);

View File

@ -116,7 +116,7 @@ static int literals_seen;
/*** Local prototypes. ***/ /*** Local prototypes. ***/
static int do_proc_packets (CTX c, iobuf_t a); static int do_proc_packets (CTX c, iobuf_t a, int keep_dek_and_list);
static void list_node (CTX c, kbnode_t node); static void list_node (CTX c, kbnode_t node);
static void proc_tree (CTX c, kbnode_t node); static void proc_tree (CTX c, kbnode_t node);
@ -580,10 +580,15 @@ proc_encrypted (CTX c, PACKET *pkt)
int early_plaintext = literals_seen; int early_plaintext = literals_seen;
unsigned int compliance_de_vs = 0; unsigned int compliance_de_vs = 0;
if (pkt->pkttype == PKT_ENCRYPTED_AEAD) if (pkt)
c->seen_pkt_encrypted_aead = 1; {
if (pkt->pkttype == PKT_ENCRYPTED_MDC) if (pkt->pkttype == PKT_ENCRYPTED_AEAD)
c->seen_pkt_encrypted_mdc = 1; c->seen_pkt_encrypted_aead = 1;
if (pkt->pkttype == PKT_ENCRYPTED_MDC)
c->seen_pkt_encrypted_mdc = 1;
}
else /* No PKT indicates the the add-recipients mode. */
log_assert (c->ctrl->modify_recipients);
if (early_plaintext) if (early_plaintext)
{ {
@ -650,6 +655,22 @@ proc_encrypted (CTX c, PACKET *pkt)
if (c->dek && opt.verbose > 1) if (c->dek && opt.verbose > 1)
log_info (_("public key encrypted data: good DEK\n")); log_info (_("public key encrypted data: good DEK\n"));
if (c->ctrl->modify_recipients)
{
if (c->anchor)
{
log_error ("command not possible with nested data\n");
write_status_errcode ("decryption.mod_recp", GPG_ERR_BAD_DATA);
xfree (c->dek);
c->dek = NULL;
return;
}
literals_seen++;
/* Simply return here. Our caller will then test for DEK and
* the PK_list to decide whether decryption worked. */
return;
}
if (!opt.show_only_session_key) if (!opt.show_only_session_key)
write_status (STATUS_BEGIN_DECRYPTION); write_status (STATUS_BEGIN_DECRYPTION);
@ -1112,7 +1133,7 @@ static int
proc_encrypt_cb (iobuf_t a, void *info ) proc_encrypt_cb (iobuf_t a, void *info )
{ {
CTX c = info; CTX c = info;
return proc_encryption_packets (c->ctrl, info, a ); return proc_encryption_packets (c->ctrl, info, a, NULL, NULL);
} }
@ -1502,7 +1523,7 @@ proc_packets (ctrl_t ctrl, void *anchor, iobuf_t a )
c->ctrl = ctrl; c->ctrl = ctrl;
c->anchor = anchor; c->anchor = anchor;
rc = do_proc_packets (c, a); rc = do_proc_packets (c, a, 0);
xfree (c); xfree (c);
return rc; return rc;
@ -1525,7 +1546,7 @@ proc_signature_packets (ctrl_t ctrl, void *anchor, iobuf_t a,
c->signed_data.used = !!signedfiles; c->signed_data.used = !!signedfiles;
c->sigfilename = sigfilename; c->sigfilename = sigfilename;
rc = do_proc_packets (c, a); rc = do_proc_packets (c, a, 0);
/* If we have not encountered any signature we print an error /* If we have not encountered any signature we print an error
messages, send a NODATA status back and return an error code. messages, send a NODATA status back and return an error code.
@ -1568,7 +1589,7 @@ proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, iobuf_t a,
c->signed_data.data_names = NULL; c->signed_data.data_names = NULL;
c->signed_data.used = (signed_data_fd != GNUPG_INVALID_FD); c->signed_data.used = (signed_data_fd != GNUPG_INVALID_FD);
rc = do_proc_packets (c, a); rc = do_proc_packets (c, a, 0);
/* If we have not encountered any signature we print an error /* If we have not encountered any signature we print an error
messages, send a NODATA status back and return an error code. messages, send a NODATA status back and return an error code.
@ -1592,8 +1613,13 @@ proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, iobuf_t a,
} }
int /* Handle encryption packets. If called recursively the caller's CTX
proc_encryption_packets (ctrl_t ctrl, void *anchor, iobuf_t a ) * should be given for ANCHOR. If R_DEK and R_LIST are not NULL the
* DEK (or NULL) is returned there and the list at R_LIST; the caller
* needs to release them; even if the function returns an error. */
gpg_error_t
proc_encryption_packets (ctrl_t ctrl, void *anchor, iobuf_t a,
DEK **r_dek, struct pubkey_enc_list **r_list)
{ {
CTX c = xmalloc_clear (sizeof *c); CTX c = xmalloc_clear (sizeof *c);
int rc; int rc;
@ -1601,7 +1627,16 @@ proc_encryption_packets (ctrl_t ctrl, void *anchor, iobuf_t a )
c->ctrl = ctrl; c->ctrl = ctrl;
c->anchor = anchor; c->anchor = anchor;
c->encrypt_only = 1; c->encrypt_only = 1;
rc = do_proc_packets (c, a); if (r_dek && r_list)
{
rc = do_proc_packets (c, a, 1);
*r_dek = c->dek;
c->dek = NULL;
*r_list = c->pkenc_list;
c->pkenc_list = NULL;
}
else
rc = do_proc_packets (c, a, 0);
xfree (c); xfree (c);
return rc; return rc;
} }
@ -1626,8 +1661,11 @@ check_nesting (CTX c)
} }
/* Main processing loop. If KEEP_DEK_AND_LIST is set the DEK and
* PKENC_LIST of the context C are not released at the end of the
* function. The caller is then required to do this. */
static int static int
do_proc_packets (CTX c, iobuf_t a) do_proc_packets (CTX c, iobuf_t a, int keep_dek_and_list)
{ {
PACKET *pkt; PACKET *pkt;
struct parse_packet_ctx_s parsectx; struct parse_packet_ctx_s parsectx;
@ -1648,6 +1686,18 @@ do_proc_packets (CTX c, iobuf_t a)
any_data = 1; any_data = 1;
if (rc) if (rc)
{ {
if (c->ctrl->modify_recipients && gpg_err_code (rc) == GPG_ERR_TRUE)
{
/* Save the last read CTB (which was the last byte
* actually read from the input) and get out of the
* loop. */
c->ctrl->last_read_ctb = parsectx.last_ctb;
/* We need to call the first part of the encrypted data
* handler to get the DEK. */
proc_encrypted (c, NULL);
rc = -1;
break;
}
free_packet (pkt, &parsectx); free_packet (pkt, &parsectx);
/* Stop processing when an invalid packet has been encountered /* Stop processing when an invalid packet has been encountered
* but don't do so when we are doing a --list-packets. */ * but don't do so when we are doing a --list-packets. */
@ -1706,8 +1756,19 @@ do_proc_packets (CTX c, iobuf_t a)
goto leave; goto leave;
case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break; case PKT_SIGNATURE: newpkt = add_signature (c, pkt); break;
case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
case PKT_PUBKEY_ENC: proc_pubkey_enc (c, pkt); break; case PKT_SYMKEY_ENC:
case PKT_PUBKEY_ENC:
/* In --add-recipients mode set the stop flag as soon as
* we see the first of these packets. */
if (c->ctrl->modify_recipients)
parsectx.only_fookey_enc = 1;
if (pkt->pkttype == PKT_SYMKEY_ENC)
proc_symkey_enc (c, pkt);
else
proc_pubkey_enc (c, pkt);
break;
case PKT_ENCRYPTED: case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC: case PKT_ENCRYPTED_MDC:
case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break; case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break;
@ -1783,8 +1844,8 @@ do_proc_packets (CTX c, iobuf_t a)
leave: leave:
release_list (c); if (!keep_dek_and_list)
xfree(c->dek); release_list (c);
free_packet (pkt, &parsectx); free_packet (pkt, &parsectx);
deinit_parse_packet (&parsectx); deinit_parse_packet (&parsectx);
xfree (pkt); xfree (pkt);

View File

@ -165,7 +165,7 @@ ask_outfile_name( const char *name, size_t namelen )
/* /*
* Make an output filename for the inputfile INAME. * Make an output filename for the inputfile INAME.
* Returns an IOBUF and an errorcode * Returns an IOBUF at A and an errorcode
* Mode 0 = use ".gpg" * Mode 0 = use ".gpg"
* 1 = use ".asc" * 1 = use ".asc"
* 2 = use ".sig" * 2 = use ".sig"

View File

@ -644,7 +644,10 @@ int proc_signature_packets (ctrl_t ctrl, void *ctx, iobuf_t a,
strlist_t signedfiles, const char *sigfile ); strlist_t signedfiles, const char *sigfile );
int proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, IOBUF a, int proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, IOBUF a,
gnupg_fd_t signed_data_fd); gnupg_fd_t signed_data_fd);
int proc_encryption_packets (ctrl_t ctrl, void *ctx, iobuf_t a); gpg_error_t proc_encryption_packets (ctrl_t ctrl, void *ctx, iobuf_t a,
DEK **r_dek,
struct pubkey_enc_list **r_list);
int list_packets( iobuf_t a ); int list_packets( iobuf_t a );
const byte *issuer_fpr_raw (PKT_signature *sig, size_t *r_len); const byte *issuer_fpr_raw (PKT_signature *sig, size_t *r_len);
@ -673,7 +676,9 @@ struct parse_packet_ctx_s
struct packet_struct last_pkt; /* The last parsed packet. */ struct packet_struct last_pkt; /* The last parsed packet. */
int free_last_pkt; /* Indicates that LAST_PKT must be freed. */ int free_last_pkt; /* Indicates that LAST_PKT must be freed. */
int skip_meta; /* Skip ring trust packets. */ int skip_meta; /* Skip ring trust packets. */
int only_fookey_enc; /* Stop if the packet is not {sym,pub}key_enc. */
unsigned int n_parsed_packets; /* Number of parsed packets. */ unsigned int n_parsed_packets; /* Number of parsed packets. */
int last_ctb; /* The last CTB read. */
}; };
typedef struct parse_packet_ctx_s *parse_packet_ctx_t; typedef struct parse_packet_ctx_s *parse_packet_ctx_t;
@ -683,7 +688,9 @@ typedef struct parse_packet_ctx_s *parse_packet_ctx_t;
(a)->last_pkt.pkt.generic= NULL;\ (a)->last_pkt.pkt.generic= NULL;\
(a)->free_last_pkt = 0; \ (a)->free_last_pkt = 0; \
(a)->skip_meta = 0; \ (a)->skip_meta = 0; \
(a)->only_fookey_enc = 0; \
(a)->n_parsed_packets = 0; \ (a)->n_parsed_packets = 0; \
(a)->last_ctb = 1; \
} while (0) } while (0)
#define deinit_parse_packet(a) do { \ #define deinit_parse_packet(a) do { \

View File

@ -763,6 +763,7 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
rc = -1; rc = -1;
goto leave; goto leave;
} }
ctx->last_ctb = ctb;
hdrlen = 0; hdrlen = 0;
hdr[hdrlen++] = ctb; hdr[hdrlen++] = ctb;
@ -774,18 +775,28 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
} }
/* Immediately following the header is the length. There are two /* Immediately following the header is the length. There are two
formats: the old format and the new format. If bit 6 (where the * formats: the old format and the new format. If bit 6 (where the
least significant bit is bit 0) is set in the tag, then we are * least significant bit is bit 0) is set in the tag, then we are
dealing with a new format packet. Otherwise, it is an old format * dealing with a new format packet. Otherwise, it is an old format
packet. */ * packet. In the new format the packet's type is encoded in the 6
* least significant bits of the tag; in the old format it is
* encoded in bits 2-5. */
pktlen = 0; pktlen = 0;
new_ctb = !!(ctb & 0x40); new_ctb = !!(ctb & 0x40);
if (new_ctb) if (new_ctb)
{ pkttype = ctb & 0x3f;
/* Get the packet's type. This is encoded in the 6 least else
significant bits of the tag. */ pkttype = (ctb >> 2) & 0xf;
pkttype = ctb & 0x3f;
if (ctx->only_fookey_enc
&& !(pkttype == PKT_SYMKEY_ENC || pkttype == PKT_PUBKEY_ENC))
{
rc = gpg_error (GPG_ERR_TRUE);
goto leave;
}
if (new_ctb)
{
/* Extract the packet's length. New format packets have 4 ways /* Extract the packet's length. New format packets have 4 ways
to encode the packet length. The value of the first byte to encode the packet length. The value of the first byte
determines the encoding and partially determines the length. determines the encoding and partially determines the length.
@ -855,12 +866,8 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
} }
} }
else else /* This is an old format packet. */
/* This is an old format packet. */
{ {
/* Extract the packet's type. This is encoded in bits 2-5. */
pkttype = (ctb >> 2) & 0xf;
/* The type of length encoding is encoded in bits 0-1 of the /* The type of length encoding is encoded in bits 0-1 of the
tag. */ tag. */
lenbytes = ((ctb & 3) == 3) ? 0 : (1 << (ctb & 3)); lenbytes = ((ctb & 3) == 3) ? 0 : (1 << (ctb & 3));