diff --git a/g10/encrypt.c b/g10/encrypt.c index 0a892c2e7..a79a47011 100644 --- a/g10/encrypt.c +++ b/g10/encrypt.c @@ -394,7 +394,7 @@ encrypt_simple (const char *filename, int mode, int use_seskey) } if (pt) pt->buf = NULL; - free_packet (&pkt); + free_packet (&pkt, NULL); xfree (cfx.dek); xfree (s2k); release_armor_context (afx); @@ -755,7 +755,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, } if (pt) pt->buf = NULL; - free_packet (&pkt); + free_packet (&pkt, NULL); xfree (cfx.dek); xfree (symkey_dek); xfree (symkey_s2k); diff --git a/g10/free-packet.c b/g10/free-packet.c index 4cf80a4a1..535a17fb8 100644 --- a/g10/free-packet.c +++ b/g10/free-packet.c @@ -394,18 +394,40 @@ free_plaintext( PKT_plaintext *pt ) xfree (pt); } + /**************** * Free the packet in PKT. */ void -free_packet (PACKET *pkt) +free_packet (PACKET *pkt, parse_packet_ctx_t parsectx) { if (!pkt || !pkt->pkt.generic) - return; + { + if (parsectx && parsectx->last_pkt) + { + if (parsectx->free_last_pkt) + { + free_packet (parsectx->last_pkt, NULL); + parsectx->free_last_pkt = 0; + } + parsectx->last_pkt = NULL; + } + return; + } if (DBG_MEMORY) log_debug ("free_packet() type=%d\n", pkt->pkttype); + /* If we have a parser context holding PKT then do not free the + * packet but set a flag that the packet in the parser context is + * now a deep copy. */ + if (parsectx && parsectx->last_pkt == pkt && !parsectx->free_last_pkt) + { + parsectx->free_last_pkt = 1; + pkt->pkt.generic = NULL; + return; + } + switch (pkt->pkttype) { case PKT_SIGNATURE: diff --git a/g10/import.c b/g10/import.c index 9aa6c8b66..3321a7eb8 100644 --- a/g10/import.c +++ b/g10/import.c @@ -790,7 +790,7 @@ read_block( IOBUF a, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys) { in_v3key = 1; ++*r_v3keys; - free_packet (pkt); + free_packet (pkt, &parsectx); init_packet (pkt); continue; } @@ -804,7 +804,7 @@ read_block( IOBUF a, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys) rc = GPG_ERR_INV_KEYRING; goto ready; } - free_packet( pkt ); + free_packet (pkt, &parsectx); init_packet(pkt); continue; } @@ -812,7 +812,7 @@ read_block( IOBUF a, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys) if (in_v3key && !(pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY)) { - free_packet( pkt ); + free_packet (pkt, &parsectx); init_packet(pkt); continue; } @@ -843,7 +843,7 @@ read_block( IOBUF a, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys) pkt->pkt.compressed->buf = NULL; push_compress_filter2(a,cfx,pkt->pkt.compressed->algorithm,1); } - free_packet( pkt ); + free_packet (pkt, &parsectx); init_packet(pkt); break; @@ -851,7 +851,7 @@ read_block( IOBUF a, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys) /* Skip those packets unless we are in restore mode. */ if ((opt.import_options & IMPORT_RESTORE)) goto x_default; - free_packet( pkt ); + free_packet (pkt, &parsectx); init_packet(pkt); break; @@ -887,7 +887,8 @@ read_block( IOBUF a, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys) release_kbnode( root ); else *ret_root = root; - free_packet( pkt ); + free_packet (pkt, &parsectx); + deinit_parse_packet (&parsectx); xfree( pkt ); return rc; } diff --git a/g10/kbnode.c b/g10/kbnode.c index b8c31b7a3..c2aaacd76 100644 --- a/g10/kbnode.c +++ b/g10/kbnode.c @@ -117,8 +117,8 @@ release_kbnode( KBNODE n ) while( n ) { n2 = n->next; if( !is_cloned_kbnode(n) ) { - free_packet( n->pkt ); - xfree( n->pkt ); + free_packet (n->pkt, NULL); + xfree( n->pkt ); } free_node( n ); n = n2; @@ -288,7 +288,7 @@ commit_kbnode( KBNODE *root ) else nl->next = n->next; if( !is_cloned_kbnode(n) ) { - free_packet( n->pkt ); + free_packet (n->pkt, NULL); xfree( n->pkt ); } free_node( n ); @@ -312,7 +312,7 @@ remove_kbnode( KBNODE *root, KBNODE node ) else nl->next = n->next; if( !is_cloned_kbnode(n) ) { - free_packet( n->pkt ); + free_packet (n->pkt, NULL); xfree( n->pkt ); } free_node( n ); diff --git a/g10/keydb.c b/g10/keydb.c index c0bc9f521..1bbda35c7 100644 --- a/g10/keydb.c +++ b/g10/keydb.c @@ -1180,7 +1180,7 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no, { if (gpg_err_code (err) == GPG_ERR_UNKNOWN_PACKET) { - free_packet (pkt); + free_packet (pkt, &parsectx); init_packet (pkt); continue; } @@ -1209,7 +1209,7 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no, the other GPG specific packets don't make sense either. */ log_error ("skipped packet of type %d in keybox\n", (int)pkt->pkttype); - free_packet(pkt); + free_packet(pkt, &parsectx); init_packet(pkt); continue; } @@ -1311,7 +1311,8 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no, release_kbnode (keyblock); else *r_keyblock = keyblock; - free_packet (pkt); + free_packet (pkt, &parsectx); + deinit_parse_packet (&parsectx); xfree (pkt); return err; } diff --git a/g10/keyedit.c b/g10/keyedit.c index 76d188927..9a6112631 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -2467,7 +2467,8 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, init_packet (pkt); init_parse_packet (&parsectx, a); err = parse_packet (&parsectx, pkt); - iobuf_close (a); + deinit_parse_packet (&parsectx); + iobuf_close (a); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char *) fname); if (!err && pkt->pkttype != PKT_SECRET_KEY && pkt->pkttype != PKT_SECRET_SUBKEY) @@ -2477,7 +2478,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, tty_printf (_("Error reading backup key from '%s': %s\n"), fname, gpg_strerror (err)); xfree (fname); - free_packet (pkt); + free_packet (pkt, NULL); xfree (pkt); break; } @@ -5008,7 +5009,7 @@ menu_expire (kbnode_t pub_keyblock, int force_mainkey, u32 newexpiration) newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; - free_packet (node->pkt); + free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; sub_pk = NULL; @@ -5114,7 +5115,7 @@ menu_changeusage (kbnode_t keyblock) newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; - free_packet (node->pkt); + free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; sub_pk = NULL; @@ -5213,7 +5214,7 @@ menu_backsign (KBNODE pub_keyblock) newpkt = xmalloc_clear (sizeof (*newpkt)); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; - free_packet (sig_pk->pkt); + free_packet (sig_pk->pkt, NULL); xfree (sig_pk->pkt); sig_pk->pkt = newpkt; @@ -5371,7 +5372,7 @@ menu_set_primary_uid (KBNODE pub_keyblock) newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; - free_packet (node->pkt); + free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; @@ -5460,7 +5461,7 @@ menu_set_preferences (KBNODE pub_keyblock) newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; - free_packet (node->pkt); + free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; @@ -5596,7 +5597,7 @@ menu_set_keyserver_url (const char *url, KBNODE pub_keyblock) newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; - free_packet (node->pkt); + free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; @@ -5797,7 +5798,7 @@ menu_set_notation (const char *string, KBNODE pub_keyblock) newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; - free_packet (node->pkt); + free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; diff --git a/g10/keygen.c b/g10/keygen.c index 44f139a19..78c35a214 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -867,7 +867,7 @@ make_backsig (PKT_signature *sig, PKT_public_key *pk, backsig_pkt.pkttype = PKT_SIGNATURE; backsig_pkt.pkt.signature = backsig; err = build_packet (backsig_out, &backsig_pkt); - free_packet (&backsig_pkt); + free_packet (&backsig_pkt, NULL); if (err) log_error ("build_packet failed for backsig: %s\n", gpg_strerror (err)); else diff --git a/g10/keyring.c b/g10/keyring.c index e4fc111ed..2210df9c0 100644 --- a/g10/keyring.c +++ b/g10/keyring.c @@ -415,7 +415,7 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) while ((rc=parse_packet (&parsectx, pkt)) != -1) { hd->found.n_packets++; if (gpg_err_code (rc) == GPG_ERR_UNKNOWN_PACKET) { - free_packet (pkt); + free_packet (pkt, &parsectx); init_packet (pkt); continue; } @@ -461,7 +461,7 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) default: log_error ("skipped packet of type %d in keyring\n", (int)pkt->pkttype); - free_packet(pkt); + free_packet(pkt, &parsectx); init_packet(pkt); continue; } @@ -490,7 +490,7 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) /* Reset LASTNODE, so that we set the cache status only from * the ring trust packet immediately following a signature. */ lastnode = NULL; - free_packet(pkt); + free_packet(pkt, &parsectx); init_packet(pkt); continue; } @@ -542,7 +542,8 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) } *ret_kb = keyblock; } - free_packet (pkt); + free_packet (pkt, &parsectx); + deinit_parse_packet (&parsectx); xfree (pkt); iobuf_close(a); @@ -1132,7 +1133,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, rc = search_packet (&parsectx, &pkt, &offset, need_uid); if (ignore_legacy && gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) { - free_packet (&pkt); + free_packet (&pkt, &parsectx); continue; } if (rc) @@ -1146,7 +1147,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, } if (initial_skip) { - free_packet (&pkt); + free_packet (&pkt, &parsectx); continue; } @@ -1228,7 +1229,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, goto found; } } - free_packet (&pkt); + free_packet (&pkt, &parsectx); continue; found: if (rc) @@ -1255,7 +1256,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, } if (n == ndesc) goto real_found; - free_packet (&pkt); + free_packet (&pkt, &parsectx); } real_found: if (!rc) @@ -1309,7 +1310,8 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, hd->current.error = rc; } - free_packet(&pkt); + free_packet (&pkt, &parsectx); + deinit_parse_packet (&parsectx); set_packet_list_mode(save_mode); return rc; } diff --git a/g10/mainproc.c b/g10/mainproc.c index 30d9b1812..8581104de 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -358,7 +358,7 @@ proc_symkey_enc (CTX c, PACKET *pkt) leave: c->symkeys++; - free_packet (pkt); + free_packet (pkt, NULL); } @@ -456,7 +456,7 @@ proc_pubkey_enc (ctrl_t ctrl, CTX c, PACKET *pkt) log_info (_("public key encrypted data: good DEK\n")); } - free_packet(pkt); + free_packet(pkt, NULL); } @@ -657,7 +657,7 @@ proc_encrypted (CTX c, PACKET *pkt) xfree (c->dek); c->dek = NULL; - free_packet (pkt); + free_packet (pkt, NULL); c->last_was_session_key = 0; write_status (STATUS_END_DECRYPTION); } @@ -774,7 +774,7 @@ proc_plaintext( CTX c, PACKET *pkt ) if (rc) log_error ("handle plaintext failed: %s\n", gpg_strerror (rc)); - free_packet(pkt); + free_packet (pkt, NULL); c->last_was_session_key = 0; /* We add a marker control packet instead of the plaintext packet. @@ -837,7 +837,7 @@ proc_compressed (CTX c, PACKET *pkt) else if (rc) log_error ("uncompressing failed: %s\n", gpg_strerror (rc)); - free_packet(pkt); + free_packet (pkt, NULL); c->last_was_session_key = 0; return rc; } @@ -1348,7 +1348,7 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a) any_data = 1; if (rc) { - free_packet (pkt); + 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. */ if (gpg_err_code (rc) == GPG_ERR_INV_PACKET @@ -1466,7 +1466,7 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a) init_packet (pkt); } else - free_packet(pkt); + free_packet (pkt, &parsectx); } if (rc == GPG_ERR_INV_PACKET) @@ -1481,7 +1481,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a) leave: release_list (c); xfree(c->dek); - free_packet (pkt); + free_packet (pkt, &parsectx); + deinit_parse_packet (&parsectx); xfree (pkt); free_md_filter_context (&c->mfx); return rc; diff --git a/g10/packet.h b/g10/packet.h index ffa1fe9d3..ad6f317e7 100644 --- a/g10/packet.h +++ b/g10/packet.h @@ -596,13 +596,22 @@ int set_packet_list_mode( int mode ); /* A context used with parse_packet. */ struct parse_packet_ctx_s { - iobuf_t inp; /* The input stream with the packets. */ + iobuf_t inp; /* The input stream with the packets. */ + PACKET *last_pkt; /* The last parsed packet. */ + int free_last_pkt; /* Indicates that LAST_PKT must be freed. */ }; typedef struct parse_packet_ctx_s *parse_packet_ctx_t; -#define init_parse_packet(a,i) do { (a)->inp = (i); \ - /**/ } while (0) +#define init_parse_packet(a,i) do { \ + (a)->inp = (i); \ + (a)->last_pkt = NULL; \ + (a)->free_last_pkt = 0; \ + } while (0) +#define deinit_parse_packet(a) do { \ + if ((a)->free_last_pkt) \ + free_packet (NULL, (a)); \ + } while (0) #if DEBUG_PARSE_PACKET @@ -803,7 +812,7 @@ void free_public_key( PKT_public_key *key ); void free_attributes(PKT_user_id *uid); void free_user_id( PKT_user_id *uid ); void free_comment( PKT_comment *rem ); -void free_packet( PACKET *pkt ); +void free_packet (PACKET *pkt, parse_packet_ctx_t parsectx); prefitem_t *copy_prefs (const prefitem_t *prefs); PKT_public_key *copy_public_key( PKT_public_key *d, PKT_public_key *s ); PKT_signature *copy_signature( PKT_signature *d, PKT_signature *s ); diff --git a/g10/parse-packet.c b/g10/parse-packet.c index 7766a45f6..ab273a5fa 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -352,6 +352,9 @@ dbg_copy_all_packets (iobuf_t inp, iobuf_t out, const char *dbg_f, int dbg_l) (rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0, "copy", dbg_f, dbg_l))); + + deinit_parse_packet (&parsectx); + return rc; } #else /*!DEBUG_PARSE_PACKET*/ @@ -372,6 +375,9 @@ copy_all_packets (iobuf_t inp, iobuf_t out) init_packet (&pkt); } while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0))); + + deinit_parse_packet (&parsectx); + return rc; } #endif /*!DEBUG_PARSE_PACKET*/ @@ -397,11 +403,17 @@ dbg_copy_some_packets (iobuf_t inp, iobuf_t out, off_t stopoff, do { if (iobuf_tell (inp) >= stopoff) - return 0; + { + deinit_parse_packet (&parsectx); + return 0; + } init_packet (&pkt); } while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0, "some", dbg_f, dbg_l))); + + deinit_parse_packet (&parsectx); + return rc; } #else /*!DEBUG_PARSE_PACKET*/ @@ -418,10 +430,16 @@ copy_some_packets (iobuf_t inp, iobuf_t out, off_t stopoff) do { if (iobuf_tell (inp) >= stopoff) - return 0; + { + deinit_parse_packet (&parsectx); + return 0; + } init_packet (&pkt); } while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0))); + + deinit_parse_packet (&parsectx); + return rc; } #endif /*!DEBUG_PARSE_PACKET*/ @@ -447,6 +465,9 @@ dbg_skip_some_packets (iobuf_t inp, unsigned n, const char *dbg_f, int dbg_l) rc = parse (&parsectx, &pkt, 0, NULL, &skip, NULL, 1, "skip", dbg_f, dbg_l); } + + deinit_parse_packet (&parsectx); + return rc; } #else /*!DEBUG_PARSE_PACKET*/ @@ -465,6 +486,9 @@ skip_some_packets (iobuf_t inp, unsigned int n) init_packet (&pkt); rc = parse (&parsectx, &pkt, 0, NULL, &skip, NULL, 1); } + + deinit_parse_packet (&parsectx); + return rc; } #endif /*!DEBUG_PARSE_PACKET*/ @@ -804,6 +828,16 @@ parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos, break; } + /* Store a shallow copy of certain packets in the context. */ + if (!rc && (pkttype == PKT_PUBLIC_KEY + || pkttype == PKT_SECRET_KEY + || pkttype == PKT_USER_ID + || pkttype == PKT_ATTRIBUTE + || pkttype == PKT_SIGNATURE)) + ctx->last_pkt = pkt; + else + ctx->last_pkt = NULL; + leave: /* FIXME: We leak in case of an error (see the xmalloc's above). */ if (!rc && iobuf_error (inp)) diff --git a/g10/sign.c b/g10/sign.c index 801c809ad..9bb1f4446 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -575,7 +575,7 @@ write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass ) pkt.pkttype = PKT_ONEPASS_SIG; pkt.pkt.onepass_sig = ops; rc = build_packet (out, &pkt); - free_packet (&pkt); + free_packet (&pkt, NULL); if (rc) { log_error ("build onepass_sig packet failed: %s\n", gpg_strerror (rc)); @@ -645,7 +645,7 @@ write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode) log_error ("build_packet(PLAINTEXT) failed: %s\n", gpg_strerror (rc) ); pt->buf = NULL; - free_packet (&pkt); + free_packet (&pkt, NULL); } else { byte copy_buffer[4096]; @@ -732,7 +732,7 @@ write_signature_packets (SK_LIST sk_list, IOBUF out, gcry_md_hd_t hash, rc = build_packet (out, &pkt); if (!rc && is_status_enabled()) print_status_sig_created (pk, sig, status_letter); - free_packet (&pkt); + free_packet (&pkt, NULL); if (rc) log_error ("build signature packet failed: %s\n", gpg_strerror (rc));