diff --git a/g10/armor.c b/g10/armor.c index ed5ef5d2e..948b8c802 100644 --- a/g10/armor.c +++ b/g10/armor.c @@ -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); +} diff --git a/g10/decrypt.c b/g10/decrypt.c index b30359af4..e232c188f 100644 --- a/g10/decrypt.c +++ b/g10/decrypt.c @@ -1,6 +1,7 @@ /* decrypt.c - decrypt and verify data * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2007, 2009 Free Software Foundation, Inc. + * Copyright (C) 2024 g10 Code GmbH * * This file is part of GnuPG. * @@ -16,6 +17,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see . + * SPDX-License-Identifier: GPL-3.0-or-later */ #include @@ -35,19 +37,26 @@ #include "../common/status.h" #include "../common/i18n.h" + + /* Assume that the input is an encrypted message and decrypt * (and if signed, verify the signature on) it. * This command differs from the default operation, as it never * writes to the filename which is included in the file and it * 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 -decrypt_message (ctrl_t ctrl, const char *filename) +gpg_error_t +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; progress_filter_context_t *pfx; - int rc; + DEK *dek = NULL; + struct pubkey_enc_list *pkenc_list = NULL; pfx = new_progress_context (); @@ -61,14 +70,18 @@ decrypt_message (ctrl_t ctrl, const char *filename) } if ( !fp ) { - rc = gpg_error_from_syserror (); + err = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), print_fname_stdin(filename), - gpg_strerror (rc)); + gpg_strerror (err)); 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 ) { @@ -86,19 +99,44 @@ decrypt_message (ctrl_t ctrl, const char *filename) } else 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) 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); release_armor_context (afx); release_progress_context (pfx); - return rc; + return err; } /* Same as decrypt_message but takes a file descriptor for input and - output. */ + output. Only used by the unfinished server mode. */ gpg_error_t decrypt_message_fd (ctrl_t ctrl, gnupg_fd_t input_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); es_fclose (opt.outfp); diff --git a/g10/encrypt.c b/g10/encrypt.c index cc8f37fe2..e4e56c8b1 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -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, 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 * 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 - can't handle it, and there isn't really any point unless we're - making a message that can be decrypted by a public key or - passphrase. */ +/* This function handles the --symmetric only (MODE true) and --store + * (MODE false) cases. We don't want to use USE_SESKEY by default + * very old gnupg versions can't handle it, and there isn't really any + * point unless we're making a message that can be decrypted by a + * public key or passphrase. */ static int 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. */ @@ -1144,22 +1253,7 @@ write_pubkey_enc (ctrl_t ctrl, else { if ( opt.verbose ) - { - 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); - } + show_encrypted_for_user_info (ctrl, pk->pubkey_usage, enc, dek); /* And write it. */ init_packet (&pkt); pkt.pkttype = PKT_PUBKEY_ENC; diff --git a/g10/filter.h b/g10/filter.h index 321b553dc..b15ce6aa5 100644 --- a/g10/filter.h +++ b/g10/filter.h @@ -175,6 +175,7 @@ void free_md_filter_context( md_filter_context_t *mfx ); armor_filter_context_t *new_armor_context (void); void release_armor_context (armor_filter_context_t *afx); 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 ); /*-- compress.c --*/ diff --git a/g10/gpg.c b/g10/gpg.c index e61b6879a..6068eefd3 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -115,6 +115,8 @@ enum cmd_and_opt_values oKnownNotation, aEncrFiles, aEncrSym, + aAddRecipients, + aChangeRecipients, aDecryptFiles, aClearsign, aStore, @@ -481,6 +483,8 @@ static gpgrt_opt_t opts[] = { ARGPARSE_c (aDecryptFiles, "decrypt-files", "@"), ARGPARSE_c (aVerify, "verify" , N_("verify a signature")), 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-public-keys", "@" ), ARGPARSE_c (aListSigs, "list-signatures", N_("list keys and signatures")), @@ -2739,6 +2743,8 @@ main (int argc, char **argv) case aExportOwnerTrust: case aImportOwnerTrust: case aRebuildKeydbCaches: + case aAddRecipients: + case aChangeRecipients: set_cmd (&cmd, pargs.r_opt); break; @@ -4168,6 +4174,12 @@ main (int argc, char **argv) case aStore: cmdname="--store"; break; + case aAddRecipients: + cmdname="--add-recipients"; + break; + case aChangeRecipients: + cmdname="--change-recipients"; + break; default: cmdname=NULL; break; @@ -4262,7 +4274,9 @@ main (int argc, char **argv) || cmd == aEncrSym || cmd == aSym || cmd == aSignSym - || cmd == aSignEncrSym, + || cmd == aSignEncrSym + || cmd == aAddRecipients + || cmd == aChangeRecipients, opt.def_cipher_algo, GCRY_CIPHER_MODE_NONE)) log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), @@ -4509,6 +4523,26 @@ main (int argc, char **argv) } 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: /* 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 @@ -4650,7 +4684,7 @@ main (int argc, char **argv) { if( argc > 1 ) wrong_args("--decrypt [filename]"); - if( (rc = decrypt_message (ctrl, fname) )) + if( (rc = decrypt_message (ctrl, fname, NULL) )) { write_status_failure ("decrypt", rc); log_error("decrypt_message failed: %s\n", gpg_strerror (rc) ); diff --git a/g10/gpg.h b/g10/gpg.h index 167cdaf28..7646aa3ee 100644 --- a/g10/gpg.h +++ b/g10/gpg.h @@ -122,6 +122,15 @@ struct server_control_s unsigned char *secret_keygrips; size_t secret_keygrips_len; 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; }; diff --git a/g10/main.h b/g10/main.h index 1c93c96e6..546a0b5b8 100644 --- a/g10/main.h +++ b/g10/main.h @@ -248,6 +248,11 @@ int encrypt_crypt (ctrl_t ctrl, gnupg_fd_t filefd, const char *filename, gnupg_fd_t outputfd); void encrypt_crypt_files (ctrl_t ctrl, 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, 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); /*-- 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, gnupg_fd_t output_fd); void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]); diff --git a/g10/mainproc.c b/g10/mainproc.c index 3bb46b574..2429e1006 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -116,7 +116,7 @@ static int literals_seen; /*** 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 proc_tree (CTX c, kbnode_t node); @@ -580,10 +580,15 @@ proc_encrypted (CTX c, PACKET *pkt) int early_plaintext = literals_seen; unsigned int compliance_de_vs = 0; - if (pkt->pkttype == PKT_ENCRYPTED_AEAD) - c->seen_pkt_encrypted_aead = 1; - if (pkt->pkttype == PKT_ENCRYPTED_MDC) - c->seen_pkt_encrypted_mdc = 1; + if (pkt) + { + if (pkt->pkttype == PKT_ENCRYPTED_AEAD) + 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) { @@ -650,6 +655,22 @@ proc_encrypted (CTX c, PACKET *pkt) if (c->dek && opt.verbose > 1) 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) write_status (STATUS_BEGIN_DECRYPTION); @@ -1112,7 +1133,7 @@ static int proc_encrypt_cb (iobuf_t a, void *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->anchor = anchor; - rc = do_proc_packets (c, a); + rc = do_proc_packets (c, a, 0); xfree (c); return rc; @@ -1525,7 +1546,7 @@ proc_signature_packets (ctrl_t ctrl, void *anchor, iobuf_t a, c->signed_data.used = !!signedfiles; 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 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.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 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 -proc_encryption_packets (ctrl_t ctrl, void *anchor, iobuf_t a ) +/* Handle encryption packets. If called recursively the caller's CTX + * 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); int rc; @@ -1601,7 +1627,16 @@ proc_encryption_packets (ctrl_t ctrl, void *anchor, iobuf_t a ) c->ctrl = ctrl; c->anchor = anchor; 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); 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 -do_proc_packets (CTX c, iobuf_t a) +do_proc_packets (CTX c, iobuf_t a, int keep_dek_and_list) { PACKET *pkt; struct parse_packet_ctx_s parsectx; @@ -1648,6 +1686,18 @@ do_proc_packets (CTX c, iobuf_t a) any_data = 1; 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); /* Stop processing when an invalid packet has been encountered * 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; 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_MDC: case PKT_ENCRYPTED_AEAD: proc_encrypted (c, pkt); break; @@ -1783,8 +1844,8 @@ do_proc_packets (CTX c, iobuf_t a) leave: - release_list (c); - xfree(c->dek); + if (!keep_dek_and_list) + release_list (c); free_packet (pkt, &parsectx); deinit_parse_packet (&parsectx); xfree (pkt); diff --git a/g10/openfile.c b/g10/openfile.c index 01f323399..42d470889 100644 --- a/g10/openfile.c +++ b/g10/openfile.c @@ -165,7 +165,7 @@ ask_outfile_name( const char *name, size_t namelen ) /* * 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" * 1 = use ".asc" * 2 = use ".sig" diff --git a/g10/packet.h b/g10/packet.h index 80238fcea..5cef17543 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -644,7 +644,10 @@ int proc_signature_packets (ctrl_t ctrl, void *ctx, iobuf_t a, strlist_t signedfiles, const char *sigfile ); int proc_signature_packets_by_fd (ctrl_t ctrl, void *anchor, IOBUF a, 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 ); 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. */ int free_last_pkt; /* Indicates that LAST_PKT must be freed. */ 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. */ + int last_ctb; /* The last CTB read. */ }; 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)->free_last_pkt = 0; \ (a)->skip_meta = 0; \ + (a)->only_fookey_enc = 0; \ (a)->n_parsed_packets = 0; \ + (a)->last_ctb = 1; \ } while (0) #define deinit_parse_packet(a) do { \ diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 8bd283b4b..a16f70fea 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -763,6 +763,7 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos, rc = -1; goto leave; } + ctx->last_ctb = ctb; hdrlen = 0; 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 - 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 - dealing with a new format packet. Otherwise, it is an old format - packet. */ + * 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 + * dealing with a new format packet. Otherwise, it is an old format + * 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; new_ctb = !!(ctb & 0x40); if (new_ctb) - { - /* Get the packet's type. This is encoded in the 6 least - significant bits of the tag. */ - pkttype = ctb & 0x3f; + pkttype = ctb & 0x3f; + else + pkttype = (ctb >> 2) & 0xf; + 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 to encode the packet length. The value of the first byte 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 - /* This is an old format packet. */ + else /* 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 tag. */ lenbytes = ((ctb & 3) == 3) ? 0 : (1 << (ctb & 3));