/* parse-packet.c - read packets
- * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
- * 2007, 2009, 2010 Free Software Foundation, Inc.
- * Copyright (C) 2014 Werner Koch
+ * Copyright (C) 1998-2007, 2009-2010 Free Software Foundation, Inc.
+ * Copyright (C) 2014, 2018 Werner Koch
+ * Copyright (C) 2015 g10 Code GmbH
*
* This file is part of GnuPG.
*
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
- * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
+ * SPDX-License-Identifier: GPL-3.0+
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
-#include <assert.h>
#include "gpg.h"
-#include "util.h"
+#include "../common/util.h"
#include "packet.h"
-#include "iobuf.h"
+#include "../common/iobuf.h"
#include "filter.h"
#include "photoid.h"
#include "options.h"
#include "main.h"
-#include "i18n.h"
+#include "../common/i18n.h"
+#include "../common/host2net.h"
+
+
+/* Maximum length of packets to avoid excessive memory allocation. */
+#define MAX_KEY_PACKET_LENGTH (256 * 1024)
+#define MAX_UID_PACKET_LENGTH ( 2 * 1024)
+#define MAX_COMMENT_PACKET_LENGTH ( 64 * 1024)
+#define MAX_ATTR_PACKET_LENGTH ( 16 * 1024*1024)
static int mpi_print_mode;
static int list_mode;
static estream_t listfp;
-static int parse (IOBUF inp, PACKET * pkt, int onlykeypkts,
+/* A linked list of known notation names. Note that the FLAG is used
+ * to store the length of the name to speed up the check. */
+static strlist_t known_notations_list;
+
+
+static int parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts,
off_t * retpos, int *skip, IOBUF out, int do_skip
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
, const char *dbg_w, const char *dbg_f, int dbg_l
#endif
);
PACKET * packet);
static int parse_comment (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet);
-static void parse_trust (IOBUF inp, int pkttype, unsigned long pktlen,
- PACKET * packet);
+static gpg_error_t parse_ring_trust (parse_packet_ctx_t ctx,
+ unsigned long pktlen);
static int parse_plaintext (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int new_ctb, int partial);
static int parse_compressed (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int new_ctb);
static int parse_encrypted (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int new_ctb, int partial);
+static gpg_error_t parse_encrypted_aead (IOBUF inp, int pkttype,
+ unsigned long pktlen, PACKET *packet,
+ int partial);
static int parse_mdc (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int new_ctb);
static int parse_gpg_control (IOBUF inp, int pkttype, unsigned long pktlen,
PACKET * packet, int partial);
+/* Read a 16-bit value in MSB order (big endian) from an iobuf. */
static unsigned short
read_16 (IOBUF inp)
{
unsigned short a;
- a = iobuf_get_noeof (inp) << 8;
+ a = (unsigned short)iobuf_get_noeof (inp) << 8;
a |= iobuf_get_noeof (inp);
return a;
}
+/* Read a 32-bit value in MSB order (big endian) from an iobuf. */
static unsigned long
read_32 (IOBUF inp)
{
unsigned long a;
- a = iobuf_get_noeof (inp) << 24;
+ a = (unsigned long)iobuf_get_noeof (inp) << 24;
a |= iobuf_get_noeof (inp) << 16;
a |= iobuf_get_noeof (inp) << 8;
a |= iobuf_get_noeof (inp);
}
-/* Read an external representation of an mpi and return the MPI. The
- * external format is a 16 bit unsigned value stored in network byte
- * order, giving the number of bits for the following integer. The
- * integer is stored with MSB first (left padded with zeroes to align
- * on a byte boundary). */
+/* Read an external representation of an MPI and return the MPI. The
+ external format is a 16-bit unsigned value stored in network byte
+ order giving the number of bits for the following integer. The
+ integer is stored MSB first and is left padded with zero bits to
+ align on a byte boundary.
+
+ The caller must set *RET_NREAD to the maximum number of bytes to
+ read from the pipeline INP. This function sets *RET_NREAD to be
+ the number of bytes actually read from the pipeline.
+
+ If SECURE is true, the integer is stored in secure memory
+ (allocated using gcry_xmalloc_secure). */
static gcry_mpi_t
mpi_read (iobuf_t inp, unsigned int *ret_nread, int secure)
{
p[1] = c2;
for (i = 0; i < nbytes; i++)
{
- p[i + 2] = iobuf_get (inp) & 0xff;
if (nread == nmax)
- goto overflow;
- nread++;
+ goto overflow;
+
+ c = iobuf_get (inp);
+ if (c == -1)
+ goto leave;
+
+ p[i + 2] = c;
+ nread ++;
}
if (gcry_mpi_scan (&a, GCRYMPI_FMT_PGP, buf, nread, &nread))
}
+/* Register STRING as a known critical notation name. */
+void
+register_known_notation (const char *string)
+{
+ strlist_t sl;
+
+ if (!known_notations_list)
+ {
+ sl = add_to_strlist (&known_notations_list,
+ "preferred-email-encoding@pgp.com");
+ sl->flags = 32;
+ sl = add_to_strlist (&known_notations_list, "pka-address@gnupg.org");
+ sl->flags = 21;
+ }
+ if (!string)
+ return; /* Only initialized the default known notations. */
+
+ /* In --set-notation we use an exclamation mark to indicate a
+ * critical notation. As a convenience skip this here. */
+ if (*string == '!')
+ string++;
+
+ if (!*string || strlist_find (known_notations_list, string))
+ return; /* Empty string or already registered. */
+
+ sl = add_to_strlist (&known_notations_list, string);
+ sl->flags = strlen (string);
+}
+
+
int
set_packet_list_mode (int mode)
{
int old = list_mode;
list_mode = mode;
- /* FIXME(gcrypt) mpi_print_mode = DBG_MPI; */
- /* We use stdout print only if invoked by the --list-packets command
+
+ /* We use stdout only if invoked by the --list-packets command
but switch to stderr in all other cases. This breaks the
previous behaviour but that seems to be more of a bug than
intentional. I don't believe that any application makes use of
this long standing annoying way of printing to stdout except when
doing a --list-packets. If this assumption fails, it will be easy
to add an option for the listing stream. Note that we initialize
- it only once; mainly because some code may switch the option
- value later back to 1 and we want to have all output to the same
- stream.
+ it only once; mainly because there is code which switches
+ opt.list_mode back to 1 and we want to have all output to the
+ same stream. The MPI_PRINT_MODE will be enabled if the
+ corresponding debug flag is set or if we are in --list-packets
+ and --verbose is given.
Using stderr is not actually very clean because it bypasses the
logging code but it is a special thing anyway. I am not sure
whether using log_stream() would be better. Perhaps we should
- enable the list mdoe only with a special option. */
+ enable the list mode only with a special option. */
if (!listfp)
- listfp = opt.list_packets == 2 ? es_stdout : es_stderr;
+ {
+ if (opt.list_packets)
+ {
+ listfp = es_stdout;
+ if (opt.verbose)
+ mpi_print_mode = 1;
+ }
+ else
+ listfp = es_stderr;
+
+ if (DBG_MPI)
+ mpi_print_mode = 1;
+ }
return old;
}
+/* If OPT.VERBOSE is set, print a warning that the algorithm ALGO is
+ not suitable for signing and encryption. */
static void
unknown_pubkey_warning (int algo)
{
}
-/* Parse a packet and return it in packet structure.
- * Returns: 0 := valid packet in pkt
- * -1 := no more packets
- * >0 := error
- * Note: The function may return an error and a partly valid packet;
- * caller must free this packet. */
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
int
-dbg_parse_packet (IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l)
+dbg_parse_packet (parse_packet_ctx_t ctx, PACKET *pkt,
+ const char *dbg_f, int dbg_l)
{
int skip, rc;
do
{
- rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l);
+ rc = parse (ctx, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l);
}
- while (skip);
+ while (skip && ! rc);
return rc;
}
#else /*!DEBUG_PARSE_PACKET*/
int
-parse_packet (IOBUF inp, PACKET * pkt)
+parse_packet (parse_packet_ctx_t ctx, PACKET *pkt)
{
int skip, rc;
do
{
- rc = parse (inp, pkt, 0, NULL, &skip, NULL, 0);
+ rc = parse (ctx, pkt, 0, NULL, &skip, NULL, 0);
}
- while (skip);
+ while (skip && ! rc);
return rc;
}
#endif /*!DEBUG_PARSE_PACKET*/
* Like parse packet, but only return secret or public (sub)key
* packets.
*/
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
int
-dbg_search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid,
+dbg_search_packet (parse_packet_ctx_t ctx, PACKET *pkt,
+ off_t * retpos, int with_uid,
const char *dbg_f, int dbg_l)
{
int skip, rc;
do
{
- rc =
- parse (inp, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0, "search",
- dbg_f, dbg_l);
+ rc = parse (ctx, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0, "search",
+ dbg_f, dbg_l);
}
- while (skip);
+ while (skip && ! rc);
return rc;
}
#else /*!DEBUG_PARSE_PACKET*/
int
-search_packet (IOBUF inp, PACKET * pkt, off_t * retpos, int with_uid)
+search_packet (parse_packet_ctx_t ctx, PACKET *pkt,
+ off_t * retpos, int with_uid)
{
int skip, rc;
do
{
- rc = parse (inp, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0);
+ rc = parse (ctx, pkt, with_uid ? 2 : 1, retpos, &skip, NULL, 0);
}
- while (skip);
+ while (skip && ! rc);
return rc;
}
#endif /*!DEBUG_PARSE_PACKET*/
/*
* Copy all packets from INP to OUT, thereby removing unused spaces.
*/
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
int
-dbg_copy_all_packets (IOBUF inp, IOBUF out, const char *dbg_f, int dbg_l)
+dbg_copy_all_packets (iobuf_t inp, iobuf_t out, const char *dbg_f, int dbg_l)
{
PACKET pkt;
+ struct parse_packet_ctx_s parsectx;
int skip, rc = 0;
+
+ if (! out)
+ log_bug ("copy_all_packets: OUT may not be NULL.\n");
+
+ init_parse_packet (&parsectx, inp);
+
do
{
init_packet (&pkt);
}
while (!
(rc =
- parse (inp, &pkt, 0, NULL, &skip, out, 0, "copy", dbg_f, dbg_l)));
+ parse (&parsectx, &pkt, 0, NULL, &skip, out, 0, "copy",
+ dbg_f, dbg_l)));
+
+ deinit_parse_packet (&parsectx);
+
return rc;
}
#else /*!DEBUG_PARSE_PACKET*/
int
-copy_all_packets (IOBUF inp, IOBUF out)
+copy_all_packets (iobuf_t inp, iobuf_t out)
{
PACKET pkt;
+ struct parse_packet_ctx_s parsectx;
int skip, rc = 0;
+
+ if (! out)
+ log_bug ("copy_all_packets: OUT may not be NULL.\n");
+
+ init_parse_packet (&parsectx, inp);
+
do
{
init_packet (&pkt);
}
- while (!(rc = parse (inp, &pkt, 0, NULL, &skip, out, 0)));
+ while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0)));
+
+ deinit_parse_packet (&parsectx);
+
return rc;
}
#endif /*!DEBUG_PARSE_PACKET*/
* Stop at offset STOPoff (i.e. don't copy packets at this or later
* offsets)
*/
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
int
-dbg_copy_some_packets (IOBUF inp, IOBUF out, off_t stopoff,
+dbg_copy_some_packets (iobuf_t inp, iobuf_t out, off_t stopoff,
const char *dbg_f, int dbg_l)
{
+ int rc = 0;
PACKET pkt;
- int skip, rc = 0;
+ int skip;
+ struct parse_packet_ctx_s parsectx;
+
+ init_parse_packet (&parsectx, inp);
+
do
{
if (iobuf_tell (inp) >= stopoff)
- return 0;
+ {
+ deinit_parse_packet (&parsectx);
+ return 0;
+ }
init_packet (&pkt);
}
- while (!(rc = parse (inp, &pkt, 0, NULL, &skip, out, 0,
+ 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*/
int
-copy_some_packets (IOBUF inp, IOBUF out, off_t stopoff)
+copy_some_packets (iobuf_t inp, iobuf_t out, off_t stopoff)
{
+ int rc = 0;
PACKET pkt;
- int skip, rc = 0;
+ struct parse_packet_ctx_s parsectx;
+ int skip;
+
+ init_parse_packet (&parsectx, inp);
+
do
{
if (iobuf_tell (inp) >= stopoff)
- return 0;
+ {
+ deinit_parse_packet (&parsectx);
+ return 0;
+ }
init_packet (&pkt);
}
- while (!(rc = parse (inp, &pkt, 0, NULL, &skip, out, 0)));
+ while (!(rc = parse (&parsectx, &pkt, 0, NULL, &skip, out, 0)));
+
+ deinit_parse_packet (&parsectx);
+
return rc;
}
#endif /*!DEBUG_PARSE_PACKET*/
/*
* Skip over N packets
*/
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
int
-dbg_skip_some_packets (IOBUF inp, unsigned n, const char *dbg_f, int dbg_l)
+dbg_skip_some_packets (iobuf_t inp, unsigned n, const char *dbg_f, int dbg_l)
{
- int skip, rc = 0;
+ int rc = 0;
+ int skip;
PACKET pkt;
+ struct parse_packet_ctx_s parsectx;
+
+ init_parse_packet (&parsectx, inp);
for (; n && !rc; n--)
{
init_packet (&pkt);
- rc = parse (inp, &pkt, 0, NULL, &skip, NULL, 1, "skip", dbg_f, 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*/
int
-skip_some_packets (IOBUF inp, unsigned n)
+skip_some_packets (iobuf_t inp, unsigned int n)
{
- int skip, rc = 0;
+ int rc = 0;
+ int skip;
PACKET pkt;
+ struct parse_packet_ctx_s parsectx;
+
+ init_parse_packet (&parsectx, inp);
for (; n && !rc; n--)
{
init_packet (&pkt);
- rc = parse (inp, &pkt, 0, NULL, &skip, NULL, 1);
+ rc = parse (&parsectx, &pkt, 0, NULL, &skip, NULL, 1);
}
+
+ deinit_parse_packet (&parsectx);
+
return rc;
}
#endif /*!DEBUG_PARSE_PACKET*/
-/*
- * Parse packet. Stores 1 at SKIP 1 if the packet should be skipped;
- * this is the case if either ONLYKEYPKTS is set and the parsed packet
- * isn't a key packet or the packet-type is 0, indicating deleted
- * stuff. If OUT is not NULL, a special copymode is used.
- */
+/* Parse a packet and save it in *PKT.
+
+ If OUT is not NULL and the packet is valid (its type is not 0),
+ then the header, the initial length field and the packet's contents
+ are written to OUT. In this case, the packet is not saved in *PKT.
+
+ ONLYKEYPKTS is a simple packet filter. If ONLYKEYPKTS is set to 1,
+ then only public subkey packets, public key packets, private subkey
+ packets and private key packets are parsed. The rest are skipped
+ (i.e., the header and the contents are read from the pipeline and
+ discarded). If ONLYKEYPKTS is set to 2, then in addition to the
+ above 4 types of packets, user id packets are also accepted.
+
+ DO_SKIP is a more coarse grained filter. Unless ONLYKEYPKTS is set
+ to 2 and the packet is a user id packet, all packets are skipped.
+
+ Finally, if a packet is invalid (it's type is 0), it is skipped.
+
+ If a packet is skipped and SKIP is not NULL, then *SKIP is set to
+ 1.
+
+ Note: ONLYKEYPKTS and DO_SKIP are only respected if OUT is NULL,
+ i.e., the packets are not simply being copied.
+
+ If RETPOS is not NULL, then the position of CTX->INP (as returned by
+ iobuf_tell) is saved there before any data is read from CTX->INP.
+ */
static int
-parse (IOBUF inp, PACKET * pkt, int onlykeypkts, off_t * retpos,
+parse (parse_packet_ctx_t ctx, PACKET *pkt, int onlykeypkts, off_t * retpos,
int *skip, IOBUF out, int do_skip
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
, const char *dbg_w, const char *dbg_f, int dbg_l
#endif
)
{
- int rc = 0, c, ctb, pkttype, lenbytes;
+ int rc = 0;
+ iobuf_t inp;
+ int c, ctb, pkttype, lenbytes;
unsigned long pktlen;
byte hdr[8];
int hdrlen;
off_t pos;
*skip = 0;
- assert (!pkt->pkt.generic);
+ inp = ctx->inp;
+
+ again:
+ log_assert (!pkt->pkt.generic);
if (retpos || list_mode)
{
pos = iobuf_tell (inp);
else
pos = 0; /* (silence compiler warning) */
+ /* The first byte of a packet is the so-called tag. The highest bit
+ must be set. */
if ((ctb = iobuf_get (inp)) == -1)
{
rc = -1;
}
hdrlen = 0;
hdr[hdrlen++] = ctb;
+
if (!(ctb & 0x80))
{
log_error ("%s: invalid packet (ctb=%02x)\n", iobuf_where (inp), ctb);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
+
+ /* 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. */
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;
+
+ /* 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.
+ See section 4.2.2 of RFC 4880 for details. */
if ((c = iobuf_get (inp)) == -1)
{
log_error ("%s: 1st length byte missing\n", iobuf_where (inp));
}
else if (c == 255)
{
- pktlen = (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 24;
- pktlen |= (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 16;
- pktlen |= (hdr[hdrlen++] = iobuf_get_noeof (inp)) << 8;
- if ((c = iobuf_get (inp)) == -1)
+ int i;
+ char value[4];
+
+ for (i = 0; i < 4; i ++)
{
- log_error ("%s: 4 byte length invalid\n", iobuf_where (inp));
- rc = gpg_error (GPG_ERR_INV_PACKET);
- goto leave;
+ if ((c = iobuf_get (inp)) == -1)
+ {
+ log_error ("%s: 4 byte length invalid\n", iobuf_where (inp));
+ rc = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
+ }
+ value[i] = hdr[hdrlen++] = c;
}
- pktlen |= (hdr[hdrlen++] = c);
+
+ pktlen = buf32_to_ulong (value);
}
else /* Partial body length. */
{
case PKT_PLAINTEXT:
case PKT_ENCRYPTED:
case PKT_ENCRYPTED_MDC:
+ case PKT_ENCRYPTED_AEAD:
case PKT_COMPRESSED:
- iobuf_set_partial_block_mode (inp, c & 0xff);
+ iobuf_set_partial_body_length_mode (inp, c & 0xff);
pktlen = 0; /* To indicate partial length. */
partial = 1;
break;
default:
- log_error ("%s: partial length for invalid"
+ log_error ("%s: partial length invalid for"
" packet type %d\n", iobuf_where (inp), pkttype);
rc = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
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));
if (!lenbytes)
{
for (; lenbytes; lenbytes--)
{
pktlen <<= 8;
- pktlen |= hdr[hdrlen++] = iobuf_get_noeof (inp);
+ c = iobuf_get (inp);
+ if (c == -1)
+ {
+ log_error ("%s: length invalid\n", iobuf_where (inp));
+ rc = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
+ }
+ pktlen |= hdr[hdrlen++] = c;
}
}
}
- if (pktlen == (unsigned long) (-1))
+ /* Sometimes the decompressing layer enters an error state in which
+ it simply outputs 0xff for every byte read. If we have a stream
+ of 0xff bytes, then it will be detected as a new format packet
+ with type 63 and a 4-byte encoded length that is 4G-1. Since
+ packets with type 63 are private and we use them as a control
+ packet, which won't be 4 GB, we reject such packets as
+ invalid. */
+ if (pkttype == 63 && pktlen == 0xFFFFFFFF)
{
/* With some probability this is caused by a problem in the
* the uncompressing layer - in some error cases it just loops
if (out && pkttype)
{
+ /* This type of copying won't work if the packet uses a partial
+ body length. (In other words, this only works if HDR is
+ actually the length.) Currently, no callers require this
+ functionality so we just log this as an error. */
+ if (partial)
+ {
+ log_error ("parse: Can't copy partial packet. Aborting.\n");
+ rc = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
+ }
+
rc = iobuf_write (out, hdr, hdrlen);
if (!rc)
rc = copy_packet (inp, out, pkttype, pktlen, partial);
}
if (with_uid && pkttype == PKT_USER_ID)
+ /* If ONLYKEYPKTS is set to 2, then we never skip user id packets,
+ even if DO_SKIP is set. */
;
else if (do_skip
+ /* type==0 is not allowed. This is an invalid packet. */
|| !pkttype
+ /* When ONLYKEYPKTS is set, we don't skip keys. */
|| (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY
&& pkttype != PKT_PUBLIC_KEY
&& pkttype != PKT_SECRET_SUBKEY && pkttype != PKT_SECRET_KEY))
if (DBG_PACKET)
{
-#ifdef DEBUG_PARSE_PACKET
+#if DEBUG_PARSE_PACKET
log_debug ("parse_packet(iob=%d): type=%d length=%lu%s (%s.%s.%d)\n",
iobuf_id (inp), pkttype, pktlen, new_ctb ? " (new_ctb)" : "",
dbg_w, dbg_f, dbg_l);
if (list_mode)
es_fprintf (listfp, "# off=%lu ctb=%02x tag=%d hlen=%d plen=%lu%s%s\n",
(unsigned long)pos, ctb, pkttype, hdrlen, pktlen,
- partial? " partial":"",
+ partial? (new_ctb ? " partial" : " indeterminate") :"",
new_ctb? " new-ctb":"");
+ /* Count it. */
+ ctx->n_parsed_packets++;
+
pkt->pkttype = pkttype;
- rc = G10ERR_UNKNOWN_PACKET; /* default error */
+ rc = GPG_ERR_UNKNOWN_PACKET; /* default error */
switch (pkttype)
{
case PKT_PUBLIC_KEY:
rc = parse_comment (inp, pkttype, pktlen, pkt);
break;
case PKT_RING_TRUST:
- parse_trust (inp, pkttype, pktlen, pkt);
- rc = 0;
+ {
+ rc = parse_ring_trust (ctx, pktlen);
+ if (!rc)
+ goto again; /* Directly read the next packet. */
+ }
break;
case PKT_PLAINTEXT:
rc = parse_plaintext (inp, pkttype, pktlen, pkt, new_ctb, partial);
case PKT_MDC:
rc = parse_mdc (inp, pkttype, pktlen, pkt, new_ctb);
break;
+ case PKT_ENCRYPTED_AEAD:
+ rc = parse_encrypted_aead (inp, pkttype, pktlen, pkt, partial);
+ break;
case PKT_GPG_CONTROL:
rc = parse_gpg_control (inp, pkttype, pktlen, pkt, partial);
break;
rc = parse_marker (inp, pkttype, pktlen);
break;
default:
+ /* Unknown packet. Skip it. */
skip_packet (inp, pkttype, pktlen, partial);
break;
}
+ /* Store a shallow copy of certain packets in the context. */
+ free_packet (NULL, ctx);
+ if (!rc && (pkttype == PKT_PUBLIC_KEY
+ || pkttype == PKT_SECRET_KEY
+ || pkttype == PKT_USER_ID
+ || pkttype == PKT_ATTRIBUTE
+ || pkttype == PKT_SIGNATURE))
+ {
+ ctx->last_pkt = *pkt;
+ }
+
leave:
- /* FIXME: Do we leak in case of an error? */
+ /* FIXME: We leak in case of an error (see the xmalloc's above). */
if (!rc && iobuf_error (inp))
- rc = G10ERR_INV_KEYRING;
- return rc;
+ rc = GPG_ERR_INV_KEYRING;
+
+ /* FIXME: We use only the error code for now to avoid problems with
+ callers which have not been checked to always use gpg_err_code()
+ when comparing error codes. */
+ return rc == -1? -1 : gpg_err_code (rc);
}
}
+/* Copy the contents of a packet from the pipeline IN to the pipeline
+ OUT.
+
+ The header and length have already been read from INP and the
+ decoded values are given as PKGTYPE and PKTLEN.
+
+ If the packet is a partial body length packet (RFC 4880, Section
+ 4.2.2.4), then iobuf_set_partial_block_modeiobuf_set_partial_block_mode
+ should already have been called on INP and PARTIAL should be set.
+
+ If PARTIAL is set or PKTLEN is 0 and PKTTYPE is PKT_COMPRESSED,
+ copy until the first EOF is encountered on INP.
+
+ Returns 0 on success and an error code if an error occurs. */
static int
copy_packet (IOBUF inp, IOBUF out, int pkttype,
unsigned long pktlen, int partial)
if (partial)
{
- while ((n = iobuf_read (inp, buf, 100)) != -1)
+ while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1)
if ((rc = iobuf_write (out, buf, n)))
return rc; /* write error */
}
{
log_debug ("copy_packet: compressed!\n");
/* compressed packet, copy till EOF */
- while ((n = iobuf_read (inp, buf, 100)) != -1)
+ while ((n = iobuf_read (inp, buf, sizeof (buf))) != -1)
if ((rc = iobuf_write (out, buf, n)))
return rc; /* write error */
}
{
for (; pktlen; pktlen -= n)
{
- n = pktlen > 100 ? 100 : pktlen;
+ n = pktlen > sizeof (buf) ? sizeof (buf) : pktlen;
n = iobuf_read (inp, buf, n);
if (n == -1)
return gpg_error (GPG_ERR_EOF);
}
+/* Skip an unknown packet. PKTTYPE is the packet's type, PKTLEN is
+ the length of the packet's content and PARTIAL is whether partial
+ body length encoding in used (in this case PKTLEN is ignored). */
static void
skip_packet (IOBUF inp, int pkttype, unsigned long pktlen, int partial)
{
}
-/* Read PKTLEN bytes form INP and return them in a newly allocated
- buffer. In case of an error NULL is returned and a error messages
- printed. */
+/* Read PKTLEN bytes from INP and return them in a newly allocated
+ * buffer. In case of an error (including reading fewer than PKTLEN
+ * bytes from INP before EOF is returned), NULL is returned and an
+ * error message is logged. */
static void *
read_rest (IOBUF inp, size_t pktlen)
{
if (list_mode)
es_fputs (":marker packet: [invalid]\n", listfp);
iobuf_skip_rest (inp, pktlen, 0);
- return G10ERR_INVALID_PACKET;
+ return GPG_ERR_INV_PACKET;
}
{
PKT_symkey_enc *k;
int rc = 0;
- int i, version, s2kmode, cipher_algo, hash_algo, seskeylen, minlen;
+ int i, version, s2kmode, cipher_algo, aead_algo, hash_algo, seskeylen, minlen;
if (pktlen < 4)
- {
- log_error ("packet(%d) too short\n", pkttype);
- if (list_mode)
- es_fprintf (listfp, ":symkey enc packet: [too short]\n");
- rc = gpg_error (GPG_ERR_INV_PACKET);
- goto leave;
- }
+ goto too_short;
version = iobuf_get_noeof (inp);
pktlen--;
- if (version != 4)
+ if (version == 4)
+ ;
+ else if (version == 5)
+ ;
+ else
{
log_error ("packet(%d) with unknown version %d\n", pkttype, version);
if (list_mode)
}
cipher_algo = iobuf_get_noeof (inp);
pktlen--;
+ if (version == 5)
+ {
+ aead_algo = iobuf_get_noeof (inp);
+ pktlen--;
+ }
+ else
+ aead_algo = 0;
+ if (pktlen < 2)
+ goto too_short;
s2kmode = iobuf_get_noeof (inp);
pktlen--;
hash_algo = iobuf_get_noeof (inp);
+ seskeylen - 1);
k->version = version;
k->cipher_algo = cipher_algo;
+ k->aead_algo = aead_algo;
k->s2k.mode = s2kmode;
k->s2k.hash_algo = hash_algo;
if (s2kmode == 1 || s2kmode == 3)
}
if (s2kmode == 3)
{
- k->s2k.count = iobuf_get (inp);
+ k->s2k.count = iobuf_get_noeof (inp);
pktlen--;
}
k->seskeylen = seskeylen;
log_info (_("WARNING: potentially insecure symmetrically"
" encrypted session key\n"));
}
- assert (!pktlen);
+ log_assert (!pktlen);
if (list_mode)
{
es_fprintf (listfp,
- ":symkey enc packet: version %d, cipher %d, s2k %d, hash %d",
- version, cipher_algo, s2kmode, hash_algo);
+ ":symkey enc packet: version %d, cipher %d, aead %d,"
+ " s2k %d, hash %d",
+ version, cipher_algo, aead_algo, s2kmode, hash_algo);
if (seskeylen)
- es_fprintf (listfp, ", seskey %d bits", (seskeylen - 1) * 8);
+ {
+ /* To compute the size of the session key we need to know
+ * the size of the AEAD nonce which we may not know. Thus
+ * we show only the seize of the entire encrypted session
+ * key. */
+ if (aead_algo)
+ es_fprintf (listfp, ", encrypted seskey %d bytes", seskeylen);
+ else
+ es_fprintf (listfp, ", seskey %d bits", (seskeylen - 1) * 8);
+ }
es_fprintf (listfp, "\n");
if (s2kmode == 1 || s2kmode == 3)
{
leave:
iobuf_skip_rest (inp, pktlen, 0);
return rc;
+
+ too_short:
+ log_error ("packet(%d) too short\n", pkttype);
+ if (list_mode)
+ es_fprintf (listfp, ":symkey enc packet: [too short]\n");
+ rc = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
}
}
+/* Dump a subpacket to LISTFP. BUFFER contains the subpacket in
+ question and points to the type field in the subpacket header (not
+ the start of the header). TYPE is the subpacket's type with the
+ critical bit cleared. CRITICAL is the value of the CRITICAL bit.
+ BUFLEN is the length of the buffer and LENGTH is the length of the
+ subpacket according to the subpacket's header. */
static void
dump_sig_subpkt (int hashed, int type, int critical,
const byte * buffer, size_t buflen, size_t length)
case SIGSUBPKT_SIG_CREATED:
if (length >= 4)
es_fprintf (listfp, "sig created %s",
- strtimestamp (buffer_to_u32 (buffer)));
+ strtimestamp (buf32_to_u32 (buffer)));
break;
case SIGSUBPKT_SIG_EXPIRE:
if (length >= 4)
{
- if (buffer_to_u32 (buffer))
+ if (buf32_to_u32 (buffer))
es_fprintf (listfp, "sig expires after %s",
- strtimevalue (buffer_to_u32 (buffer)));
+ strtimevalue (buf32_to_u32 (buffer)));
else
es_fprintf (listfp, "sig does not expire");
}
if (!length)
p = "[invalid regexp subpacket]";
else
- es_fprintf (listfp, "regular expression: \"%s\"", buffer);
+ {
+ es_fprintf (listfp, "regular expression: \"");
+ es_write_sanitized (listfp, buffer, length, "\"", NULL);
+ p = "\"";
+ }
break;
case SIGSUBPKT_REVOCABLE:
if (length)
case SIGSUBPKT_KEY_EXPIRE:
if (length >= 4)
{
- if (buffer_to_u32 (buffer))
+ if (buf32_to_u32 (buffer))
es_fprintf (listfp, "key expires after %s",
- strtimevalue (buffer_to_u32 (buffer)));
+ strtimevalue (buf32_to_u32 (buffer)));
else
es_fprintf (listfp, "key does not expire");
}
for (i = 0; i < length; i++)
es_fprintf (listfp, " %d", buffer[i]);
break;
+ case SIGSUBPKT_PREF_AEAD:
+ es_fputs ("pref-aead-algos:", listfp);
+ for (i = 0; i < length; i++)
+ es_fprintf (listfp, " %d", buffer[i]);
+ break;
case SIGSUBPKT_REV_KEY:
es_fputs ("revocation key: ", listfp);
if (length < 22)
case SIGSUBPKT_ISSUER:
if (length >= 8)
es_fprintf (listfp, "issuer key ID %08lX%08lX",
- (ulong) buffer_to_u32 (buffer),
- (ulong) buffer_to_u32 (buffer + 4));
+ (ulong) buf32_to_u32 (buffer),
+ (ulong) buf32_to_u32 (buffer + 4));
+ break;
+ case SIGSUBPKT_ISSUER_FPR:
+ if (length >= 21)
+ {
+ char *tmp;
+ es_fprintf (listfp, "issuer fpr v%d ", buffer[0]);
+ tmp = bin2hex (buffer+1, length-1, NULL);
+ if (tmp)
+ {
+ es_fputs (tmp, listfp);
+ xfree (tmp);
+ }
+ }
break;
case SIGSUBPKT_NOTATION:
{
es_fprintf (listfp, " %d", buffer[i]);
break;
case SIGSUBPKT_KS_FLAGS:
- es_fputs ("key server preferences:", listfp);
+ es_fputs ("keyserver preferences:", listfp);
for (i = 0; i < length; i++)
es_fprintf (listfp, " %02X", buffer[i]);
break;
case SIGSUBPKT_PREF_KS:
- es_fputs ("preferred key server: ", listfp);
+ es_fputs ("preferred keyserver: ", listfp);
es_write_sanitized (listfp, buffer, length, ")", NULL);
break;
case SIGSUBPKT_PRIMARY_UID:
case SIGSUBPKT_KEY_FLAGS:
case SIGSUBPKT_KS_FLAGS:
case SIGSUBPKT_PREF_SYM:
+ case SIGSUBPKT_PREF_AEAD:
case SIGSUBPKT_PREF_HASH:
case SIGSUBPKT_PREF_COMPR:
case SIGSUBPKT_POLICY:
if (n < 8)
break;
return 0;
+ case SIGSUBPKT_ISSUER_FPR: /* issuer key ID */
+ if (n < 21)
+ break;
+ return 0;
case SIGSUBPKT_NOTATION:
/* minimum length needed, and the subpacket must be well-formed
where the name length and value length all fit inside the
/* Return true if we understand the critical notation. */
static int
-can_handle_critical_notation (const byte * name, size_t len)
+can_handle_critical_notation (const byte *name, size_t len)
{
- if (len == 32 && memcmp (name, "preferred-email-encoding@pgp.com", 32) == 0)
- return 1;
- if (len == 21 && memcmp (name, "pka-address@gnupg.org", 21) == 0)
- return 1;
+ strlist_t sl;
- return 0;
+ register_known_notation (NULL); /* Make sure it is initialized. */
+
+ for (sl = known_notations_list; sl; sl = sl->next)
+ if (sl->flags == len && !memcmp (sl->d, name, len))
+ return 1; /* Known */
+
+ if (opt.verbose)
+ {
+ log_info(_("Unknown critical signature notation: ") );
+ print_utf8_buffer (log_get_stream(), name, len);
+ log_printf ("\n");
+ }
+
+ return 0; /* Unknown. */
}
{
case SIGSUBPKT_NOTATION:
if (n >= 8)
- return can_handle_critical_notation (buffer + 8,
- (buffer[4] << 8) | buffer[5]);
- else
- return 0;
+ {
+ size_t notation_len = ((buffer[4] << 8) | buffer[5]);
+ if (n - 8 >= notation_len)
+ return can_handle_critical_notation (buffer + 8, notation_len);
+ }
+ return 0;
case SIGSUBPKT_SIGNATURE:
case SIGSUBPKT_SIG_CREATED:
case SIGSUBPKT_SIG_EXPIRE:
case SIGSUBPKT_REVOCABLE:
case SIGSUBPKT_REV_KEY:
case SIGSUBPKT_ISSUER: /* issuer key ID */
+ case SIGSUBPKT_ISSUER_FPR: /* issuer fingerprint */
case SIGSUBPKT_PREF_SYM:
+ case SIGSUBPKT_PREF_AEAD:
case SIGSUBPKT_PREF_HASH:
case SIGSUBPKT_PREF_COMPR:
case SIGSUBPKT_KEY_FLAGS:
/* Is it enough to show the policy or keyserver? */
case SIGSUBPKT_POLICY:
case SIGSUBPKT_PREF_KS:
+ case SIGSUBPKT_REVOC_REASON: /* At least we know about it. */
return 1;
default:
{
if (buflen < 4)
goto too_short;
- n = (buffer[0] << 24) | (buffer[1] << 16)
- | (buffer[2] << 8) | buffer[3];
+ n = buf32_to_size_t (buffer);
buffer += 4;
buflen -= 4;
}
}
if (buflen < n)
goto too_short;
+ if (!buflen)
+ goto no_type_byte;
type = *buffer;
if (type & 0x80)
{
buflen -= n;
}
if (reqtype == SIGSUBPKT_TEST_CRITICAL)
- return buffer; /* Used as True to indicate that there is no. */
+ /* Returning NULL means we found a subpacket with the critical bit
+ set that we don't grok. We've iterated over all the subpackets
+ and haven't found such a packet so we need to return a non-NULL
+ value. */
+ return buffer;
/* Critical bit we don't understand. */
if (start)
if (start)
*start = -1;
return NULL;
+
+ no_type_byte:
+ if (opt.verbose)
+ log_info ("type octet missing in subpacket\n");
+ if (start)
+ *start = -1;
+ return NULL;
}
const byte *
-parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype,
- size_t * ret_n)
+parse_sig_subpkt2 (PKT_signature * sig, sigsubpkttype_t reqtype)
{
const byte *p;
- p = parse_sig_subpkt (sig->hashed, reqtype, ret_n);
+ p = parse_sig_subpkt (sig->hashed, reqtype, NULL);
if (!p)
- p = parse_sig_subpkt (sig->unhashed, reqtype, ret_n);
+ p = parse_sig_subpkt (sig->unhashed, reqtype, NULL);
return p;
}
void
parse_revkeys (PKT_signature * sig)
{
- struct revocation_key *revkey;
+ const byte *revkey;
int seq = 0;
size_t len;
if (sig->sig_class != 0x1F)
return;
- while ((revkey =
- (struct revocation_key *) enum_sig_subpkt (sig->hashed,
- SIGSUBPKT_REV_KEY,
- &len, &seq, NULL)))
+ while ((revkey = enum_sig_subpkt (sig->hashed, SIGSUBPKT_REV_KEY,
+ &len, &seq, NULL)))
{
- if (len == sizeof (struct revocation_key)
- && (revkey->class & 0x80)) /* 0x80 bit must be set. */
+ /* Consider only valid packets. They must have a length of
+ * either 2+20 or 2+32 octets and bit 7 of the class octet must
+ * be set. */
+ if ((len == 22 || len == 34)
+ && (revkey[0] & 0x80))
{
sig->revkey = xrealloc (sig->revkey,
- sizeof (struct revocation_key *) *
+ sizeof (struct revocation_key) *
(sig->numrevkeys + 1));
- sig->revkey[sig->numrevkeys] = revkey;
+
+ sig->revkey[sig->numrevkeys].class = revkey[0];
+ sig->revkey[sig->numrevkeys].algid = revkey[1];
+ len -= 2;
+ sig->revkey[sig->numrevkeys].fprlen = len;
+ memcpy (sig->revkey[sig->numrevkeys].fpr, revkey+2, len);
+ memset (sig->revkey[sig->numrevkeys].fpr+len, 0,
+ sizeof (sig->revkey[sig->numrevkeys].fpr) - len);
sig->numrevkeys++;
}
}
{
int md5_len = 0;
unsigned n;
- int is_v4 = 0;
+ int is_v4or5 = 0;
int rc = 0;
int i, ndata;
}
sig->version = iobuf_get_noeof (inp);
pktlen--;
- if (sig->version == 4)
- is_v4 = 1;
+ if (sig->version == 4 || sig->version == 5)
+ is_v4or5 = 1;
else if (sig->version != 2 && sig->version != 3)
{
log_error ("packet(%d) with unknown version %d\n",
goto leave;
}
- if (!is_v4)
+ if (!is_v4or5)
{
+ if (pktlen == 0)
+ goto underflow;
md5_len = iobuf_get_noeof (inp);
pktlen--;
}
+ if (pktlen == 0)
+ goto underflow;
sig->sig_class = iobuf_get_noeof (inp);
pktlen--;
- if (!is_v4)
+ if (!is_v4or5)
{
+ if (pktlen < 12)
+ goto underflow;
sig->timestamp = read_32 (inp);
pktlen -= 4;
sig->keyid[0] = read_32 (inp);
sig->keyid[1] = read_32 (inp);
pktlen -= 4;
}
+ if (pktlen < 2)
+ goto underflow;
sig->pubkey_algo = iobuf_get_noeof (inp);
pktlen--;
sig->digest_algo = iobuf_get_noeof (inp);
pktlen--;
sig->flags.exportable = 1;
sig->flags.revocable = 1;
- if (is_v4) /* Read subpackets. */
+ if (is_v4or5) /* Read subpackets. */
{
+ if (pktlen < 2)
+ goto underflow;
n = read_16 (inp);
pktlen -= 2; /* Length of hashed data. */
+ if (pktlen < n)
+ goto underflow;
if (n > 10000)
{
log_error ("signature packet: hashed data too long\n");
if (list_mode)
es_fputs (":signature packet: [hashed data too long]\n", listfp);
- rc = G10ERR_INVALID_PACKET;
+ rc = GPG_ERR_INV_PACKET;
goto leave;
}
if (n)
}
pktlen -= n;
}
+ if (pktlen < 2)
+ goto underflow;
n = read_16 (inp);
pktlen -= 2; /* Length of unhashed data. */
+ if (pktlen < n)
+ goto underflow;
if (n > 10000)
{
log_error ("signature packet: unhashed data too long\n");
if (list_mode)
es_fputs (":signature packet: [unhashed data too long]\n", listfp);
- rc = G10ERR_INVALID_PACKET;
+ rc = GPG_ERR_INV_PACKET;
goto leave;
}
if (n)
}
}
- if (pktlen < 5) /* Sanity check. */
- {
- log_error ("packet(%d) too short\n", pkttype);
- if (list_mode)
- es_fputs (":signature packet: [too short]\n", listfp);
- rc = G10ERR_INVALID_PACKET;
- goto leave;
- }
-
+ if (pktlen < 2)
+ goto underflow;
sig->digest_start[0] = iobuf_get_noeof (inp);
pktlen--;
sig->digest_start[1] = iobuf_get_noeof (inp);
pktlen--;
- if (is_v4 && sig->pubkey_algo) /* Extract required information. */
+ if (is_v4or5 && sig->pubkey_algo) /* Extract required information. */
{
const byte *p;
size_t len;
p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_CREATED, NULL);
if (p)
- sig->timestamp = buffer_to_u32 (p);
+ sig->timestamp = buf32_to_u32 (p);
else if (!(sig->pubkey_algo >= 100 && sig->pubkey_algo <= 110)
&& opt.verbose)
log_info ("signature packet without timestamp\n");
- p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER, NULL);
+ p = parse_sig_subpkt2 (sig, SIGSUBPKT_ISSUER);
if (p)
{
- sig->keyid[0] = buffer_to_u32 (p);
- sig->keyid[1] = buffer_to_u32 (p + 4);
+ sig->keyid[0] = buf32_to_u32 (p);
+ sig->keyid[1] = buf32_to_u32 (p + 4);
}
else if (!(sig->pubkey_algo >= 100 && sig->pubkey_algo <= 110)
&& opt.verbose)
log_info ("signature packet without keyid\n");
p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL);
- if (p && buffer_to_u32 (p))
- sig->expiredate = sig->timestamp + buffer_to_u32 (p);
+ if (p && buf32_to_u32 (p))
+ sig->expiredate = sig->timestamp + buf32_to_u32 (p);
if (sig->expiredate && sig->expiredate <= make_timestamp ())
sig->flags.expired = 1;
if (p)
sig->flags.pref_ks = 1;
+ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIGNERS_UID, &len);
+ if (p && len)
+ {
+ sig->signers_uid = try_make_printable_string (p, len, 0);
+ if (!sig->signers_uid)
+ {
+ rc = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_NOTATION, NULL);
if (p)
sig->flags.notation = 1;
unhashed area. In theory, anyway, we should never see this
packet off of a local keyring. */
- p = parse_sig_subpkt2 (sig, SIGSUBPKT_EXPORTABLE, NULL);
+ p = parse_sig_subpkt2 (sig, SIGSUBPKT_EXPORTABLE);
if (p && *p == 0)
sig->flags.exportable = 0;
(ulong) sig->keyid[0], (ulong) sig->keyid[1],
sig->version, (ulong) sig->timestamp, md5_len, sig->sig_class,
sig->digest_algo, sig->digest_start[0], sig->digest_start[1]);
- if (is_v4)
+ if (is_v4or5)
{
parse_sig_subpkt (sig->hashed, SIGSUBPKT_LIST_HASHED, NULL);
parse_sig_subpkt (sig->unhashed, SIGSUBPKT_LIST_UNHASHED, NULL);
/* We include a limit to avoid too trivial DoS attacks by
having gpg allocate too much memory. */
log_error ("signature packet: too much data\n");
- rc = G10ERR_INVALID_PACKET;
+ rc = GPG_ERR_INV_PACKET;
}
else
{
es_putc ('\n', listfp);
}
if (!sig->data[i])
- rc = G10ERR_INVALID_PACKET;
+ rc = GPG_ERR_INV_PACKET;
}
}
leave:
iobuf_skip_rest (inp, pktlen, 0);
return rc;
+
+ underflow:
+ log_error ("packet(%d) too short\n", pkttype);
+ if (list_mode)
+ es_fputs (":signature packet: [too short]\n", listfp);
+
+ iobuf_skip_rest (inp, pktlen, 0);
+
+ return GPG_ERR_INV_PACKET;
}
int i, version, algorithm;
unsigned long timestamp, expiredate, max_expiredate;
int npkey, nskey;
- int rc = 0;
u32 keyid[2];
PKT_public_key *pk;
else if (version == 4)
{
/* The only supported version. Use an older gpg
- versions (i.e. gpg 1.4 to parse v3 packets). */
+ version (i.e. gpg 1.4) to parse v3 packets. */
}
else if (version == 2 || version == 3)
{
log_info ("packet(%d) with obsolete version %d\n", pkttype, version);
if (list_mode)
es_fprintf (listfp, ":key packet: [obsolete version %d]\n", version);
- err = gpg_error (GPG_ERR_INV_PACKET);
+ pk->version = version;
+ err = gpg_error (GPG_ERR_LEGACY_KEY);
goto leave;
}
else
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
+ else if (pktlen > MAX_KEY_PACKET_LENGTH)
+ {
+ log_error ("packet(%d) too large\n", pkttype);
+ if (list_mode)
+ es_fputs (":key packet: [too large]\n", listfp);
+ err = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
+ }
timestamp = read_32 (inp);
pktlen -= 4;
|| algorithm == PUBKEY_ALGO_ECDH) && i==0)
{
char *curve = openpgp_oid_to_str (pk->pkey[0]);
- es_fprintf (listfp, " %s (%s)",
- openpgp_oid_to_curve (curve), curve);
+ const char *name = openpgp_oid_to_curve (curve, 0);
+ es_fprintf (listfp, " %s (%s)", name?name:"", curve);
xfree (curve);
}
es_putc ('\n', listfp);
byte temp[16];
size_t snlen = 0;
+ if (pktlen < 1)
+ {
+ err = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
+ }
+
pk->seckey_info = ski = xtrycalloc (1, sizeof *ski);
if (!pk->seckey_info)
{
case 3:
for (i = 0; i < 8 && pktlen; i++, pktlen--)
temp[i] = iobuf_get_noeof (inp);
+ if (i < 8)
+ {
+ err = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
+ }
memcpy (ski->s2k.salt, temp, 8);
break;
}
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
- ski->s2k.count = iobuf_get (inp);
+ ski->s2k.count = iobuf_get_noeof (inp);
pktlen--;
if (list_mode)
es_fprintf (listfp, "\tprotect count: %lu (%lu)\n",
* NOTE: if you change the ivlen above 16, don't forget to
* enlarge temp. */
ski->ivlen = openpgp_cipher_blocklen (ski->algo);
- assert (ski->ivlen <= sizeof (temp));
+ log_assert (ski->ivlen <= sizeof (temp));
if (ski->s2k.mode == 1001)
ski->ivlen = 0;
err = gpg_error (GPG_ERR_INV_PACKET);
goto leave;
}
- for (i = 0; i < ski->ivlen && pktlen; i++, pktlen--)
+ for (i = 0; i < ski->ivlen; i++, pktlen--)
temp[i] = iobuf_get_noeof (inp);
if (list_mode)
{
}
else if (ski->is_protected)
{
+ if (pktlen < 2) /* At least two bytes for the length. */
+ {
+ err = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
+ }
+
/* Ugly: The length is encrypted too, so we read all stuff
* up to the end of the packet into the first SKEY
* element. */
/* Not encrypted. */
for (i = npkey; i < nskey; i++)
{
- unsigned int n = pktlen;
+ unsigned int n;
+
+ if (pktlen < 2) /* At least two bytes for the length. */
+ {
+ err = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
+ }
+ n = pktlen;
pk->pkey[i] = mpi_read (inp, &n, 0);
pktlen -= n;
if (list_mode)
if (err)
goto leave;
+ if (pktlen < 2)
+ {
+ err = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
+ }
ski->csum = read_16 (inp);
pktlen -= 2;
if (list_mode)
}
}
+ /* Note that KEYID below has been initialized above in list_mode. */
if (list_mode)
es_fprintf (listfp, "\tkeyid: %08lX%08lX\n",
(ulong) keyid[0], (ulong) keyid[1]);
leave:
iobuf_skip_rest (inp, pktlen, 0);
- return rc;
+ return err;
}
{
if (buflen < 4)
goto too_short;
- n = (buffer[0] << 24) | (buffer[1] << 16)
- | (buffer[2] << 8) | buffer[3];
+ n = buf32_to_size_t (buffer);
buffer += 4;
buflen -= 4;
}
if (buflen < n)
goto too_short;
- attribs =
- xrealloc (attribs, (count + 1) * sizeof (struct user_attribute));
+ if (!n)
+ {
+ /* Too short to encode the subpacket type. */
+ if (opt.verbose)
+ log_info ("attribute subpacket too short\n");
+ break;
+ }
+
+ attribs = xrealloc (attribs,
+ (count + 1) * sizeof (struct user_attribute));
memset (&attribs[count], 0, sizeof (struct user_attribute));
type = *buffer;
allocatable, and a very large pktlen could actually cause our
allocation to wrap around in xmalloc to a small number. */
- if (pktlen > 2048)
+ if (pktlen > MAX_UID_PACKET_LENGTH)
{
log_error ("packet(%d) too large\n", pkttype);
if (list_mode)
es_fprintf (listfp, ":user ID packet: [too large]\n");
iobuf_skip_rest (inp, pktlen, 0);
- return G10ERR_INVALID_PACKET;
+ return GPG_ERR_INV_PACKET;
}
packet->pkt.user_id = xmalloc_clear (sizeof *packet->pkt.user_id + pktlen);
void
make_attribute_uidname (PKT_user_id * uid, size_t max_namelen)
{
- assert (max_namelen > 70);
+ log_assert (max_namelen > 70);
if (uid->numattribs <= 0)
sprintf (uid->name, "[bad attribute packet of size %lu]",
uid->attrib_len);
/* We better cap the size of an attribute packet to make DoS not too
easy. 16MB should be more then enough for one attribute packet
(ie. a photo). */
- if (pktlen > 16*1024*1024)
+ if (pktlen > MAX_ATTR_PACKET_LENGTH)
{
log_error ("packet(%d) too large\n", pkttype);
if (list_mode)
es_fprintf (listfp, ":attribute packet: [too large]\n");
iobuf_skip_rest (inp, pktlen, 0);
- return G10ERR_INVALID_PACKET;
+ return GPG_ERR_INV_PACKET;
}
#define EXTRA_UID_NAME_SPACE 71
overflow in the malloc below. Comment packets are actually not
anymore define my OpenPGP and we even stopped to use our
private comment packet. */
- if (pktlen > 65536)
+ if (pktlen > MAX_COMMENT_PACKET_LENGTH)
{
log_error ("packet(%d) too large\n", pkttype);
if (list_mode)
es_fprintf (listfp, ":%scomment packet: [too large]\n",
pkttype == PKT_OLD_COMMENT ? "OpenPGP draft " : "");
iobuf_skip_rest (inp, pktlen, 0);
- return G10ERR_INVALID_PACKET;
+ return GPG_ERR_INV_PACKET;
}
packet->pkt.comment = xmalloc (sizeof *packet->pkt.comment + pktlen - 1);
packet->pkt.comment->len = pktlen;
}
-static void
-parse_trust (IOBUF inp, int pkttype, unsigned long pktlen, PACKET * pkt)
+/* Parse a ring trust packet RFC4880 (5.10).
+ *
+ * This parser is special in that the packet is not stored as a packet
+ * but its content is merged into the previous packet. */
+static gpg_error_t
+parse_ring_trust (parse_packet_ctx_t ctx, unsigned long pktlen)
{
+ gpg_error_t err;
+ iobuf_t inp = ctx->inp;
+ PKT_ring_trust rt = {0};
int c;
+ int not_gpg = 0;
- (void) pkttype;
+ if (!pktlen)
+ {
+ if (list_mode)
+ es_fprintf (listfp, ":trust packet: empty\n");
+ err = 0;
+ goto leave;
+ }
+ c = iobuf_get_noeof (inp);
+ pktlen--;
+ rt.trustval = c;
if (pktlen)
{
- c = iobuf_get_noeof (inp);
+ if (!c)
+ {
+ c = iobuf_get_noeof (inp);
+ /* We require that bit 7 of the sigcache is 0 (easier
+ * eof handling). */
+ if (!(c & 0x80))
+ rt.sigcache = c;
+ }
+ else
+ iobuf_get_noeof (inp); /* Dummy read. */
pktlen--;
- pkt->pkt.ring_trust = xmalloc (sizeof *pkt->pkt.ring_trust);
- pkt->pkt.ring_trust->trustval = c;
- pkt->pkt.ring_trust->sigcache = 0;
- if (!c && pktlen == 1)
- {
- c = iobuf_get_noeof (inp);
- pktlen--;
- /* We require that bit 7 of the sigcache is 0 (easier eof
- handling). */
- if (!(c & 0x80))
- pkt->pkt.ring_trust->sigcache = c;
- }
- if (list_mode)
- es_fprintf (listfp, ":trust packet: flag=%02x sigcache=%02x\n",
- pkt->pkt.ring_trust->trustval,
- pkt->pkt.ring_trust->sigcache);
}
- else
+
+ /* Next is the optional subtype. */
+ if (pktlen > 3)
{
- if (list_mode)
- es_fprintf (listfp, ":trust packet: empty\n");
+ char tmp[4];
+ tmp[0] = iobuf_get_noeof (inp);
+ tmp[1] = iobuf_get_noeof (inp);
+ tmp[2] = iobuf_get_noeof (inp);
+ tmp[3] = iobuf_get_noeof (inp);
+ pktlen -= 4;
+ if (!memcmp (tmp, "gpg", 3))
+ rt.subtype = tmp[3];
+ else
+ not_gpg = 1;
}
+ /* If it is a key or uid subtype read the remaining data. */
+ if ((rt.subtype == RING_TRUST_KEY || rt.subtype == RING_TRUST_UID)
+ && pktlen >= 6 )
+ {
+ int i;
+ unsigned int namelen;
+
+ rt.keyorg = iobuf_get_noeof (inp);
+ pktlen--;
+ rt.keyupdate = read_32 (inp);
+ pktlen -= 4;
+ namelen = iobuf_get_noeof (inp);
+ pktlen--;
+ if (namelen && pktlen)
+ {
+ rt.url = xtrymalloc (namelen + 1);
+ if (!rt.url)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ for (i = 0; pktlen && i < namelen; pktlen--, i++)
+ rt.url[i] = iobuf_get_noeof (inp);
+ rt.url[i] = 0;
+ }
+ }
+
+ if (list_mode)
+ {
+ if (rt.subtype == RING_TRUST_SIG)
+ es_fprintf (listfp, ":trust packet: sig flag=%02x sigcache=%02x\n",
+ rt.trustval, rt.sigcache);
+ else if (rt.subtype == RING_TRUST_UID || rt.subtype == RING_TRUST_KEY)
+ {
+ unsigned char *p;
+
+ es_fprintf (listfp, ":trust packet: %s upd=%lu src=%d%s",
+ (rt.subtype == RING_TRUST_UID? "uid" : "key"),
+ (unsigned long)rt.keyupdate,
+ rt.keyorg,
+ (rt.url? " url=":""));
+ if (rt.url)
+ {
+ for (p = rt.url; *p; p++)
+ {
+ if (*p >= ' ' && *p <= 'z')
+ es_putc (*p, listfp);
+ else
+ es_fprintf (listfp, "\\x%02x", *p);
+ }
+ }
+ es_putc ('\n', listfp);
+ }
+ else if (not_gpg)
+ es_fprintf (listfp, ":trust packet: not created by gpg\n");
+ else
+ es_fprintf (listfp, ":trust packet: subtype=%02x\n",
+ rt.subtype);
+ }
+
+ /* Now transfer the data to the respective packet. Do not do this
+ * if SKIP_META is set. */
+ if (!ctx->last_pkt.pkt.generic || ctx->skip_meta)
+ ;
+ else if (rt.subtype == RING_TRUST_SIG
+ && ctx->last_pkt.pkttype == PKT_SIGNATURE)
+ {
+ PKT_signature *sig = ctx->last_pkt.pkt.signature;
+
+ if ((rt.sigcache & 1))
+ {
+ sig->flags.checked = 1;
+ sig->flags.valid = !!(rt.sigcache & 2);
+ }
+ }
+ else if (rt.subtype == RING_TRUST_UID
+ && (ctx->last_pkt.pkttype == PKT_USER_ID
+ || ctx->last_pkt.pkttype == PKT_ATTRIBUTE))
+ {
+ PKT_user_id *uid = ctx->last_pkt.pkt.user_id;
+
+ uid->keyorg = rt.keyorg;
+ uid->keyupdate = rt.keyupdate;
+ uid->updateurl = rt.url;
+ rt.url = NULL;
+ }
+ else if (rt.subtype == RING_TRUST_KEY
+ && (ctx->last_pkt.pkttype == PKT_PUBLIC_KEY
+ || ctx->last_pkt.pkttype == PKT_SECRET_KEY))
+ {
+ PKT_public_key *pk = ctx->last_pkt.pkt.public_key;
+
+ pk->keyorg = rt.keyorg;
+ pk->keyupdate = rt.keyupdate;
+ pk->updateurl = rt.url;
+ rt.url = NULL;
+ }
+
+ err = 0;
+
+ leave:
+ xfree (rt.url);
+ free_packet (NULL, ctx); /* This sets ctx->last_pkt to NULL. */
iobuf_skip_rest (inp, pktlen, 0);
+ return err;
}
else
pt->name[i] = c;
}
+ /* Fill up NAME so that a check with valgrind won't complain about
+ * reading from uninitialized memory. This case may be triggred by
+ * corrupted packets. */
+ for (; i < namelen; i++)
+ pt->name[i] = 0;
+
pt->timestamp = read_32 (inp);
if (pktlen)
pktlen -= 4;
pt->len = pktlen;
pt->buf = inp;
- pktlen = 0;
if (list_mode)
{
ed->buf = NULL;
ed->new_ctb = new_ctb;
ed->is_partial = partial;
+ ed->aead_algo = 0;
+ ed->cipher_algo = 0; /* Only used with AEAD. */
+ ed->chunkbyte = 0; /* Only used with AEAD. */
if (pkttype == PKT_ENCRYPTED_MDC)
{
/* Fixme: add some pktlen sanity checks. */
log_error ("packet(%d) too short\n", pkttype);
if (list_mode)
es_fputs (":encrypted data packet: [too short]\n", listfp);
- rc = G10ERR_INVALID_PACKET;
+ rc = GPG_ERR_INV_PACKET;
iobuf_skip_rest (inp, pktlen, partial);
goto leave;
}
}
+static gpg_error_t
+parse_encrypted_aead (iobuf_t inp, int pkttype, unsigned long pktlen,
+ PACKET *pkt, int partial)
+{
+ int rc = 0;
+ PKT_encrypted *ed;
+ unsigned long orig_pktlen = pktlen;
+ int version;
+
+ ed = pkt->pkt.encrypted = xtrymalloc (sizeof *pkt->pkt.encrypted);
+ if (!ed)
+ return gpg_error_from_syserror ();
+ ed->len = 0;
+ ed->extralen = 0; /* (only used in build_packet.) */
+ ed->buf = NULL;
+ ed->new_ctb = 1; /* (packet number requires a new CTB anyway.) */
+ ed->is_partial = partial;
+ ed->mdc_method = 0;
+ /* A basic sanity check. We need one version byte, one algo byte,
+ * one aead algo byte, one chunkbyte, at least 15 byte IV. */
+ if (orig_pktlen && pktlen < 19)
+ {
+ log_error ("packet(%d) too short\n", pkttype);
+ if (list_mode)
+ es_fputs (":aead encrypted packet: [too short]\n", listfp);
+ rc = gpg_error (GPG_ERR_INV_PACKET);
+ iobuf_skip_rest (inp, pktlen, partial);
+ goto leave;
+ }
+
+ version = iobuf_get_noeof (inp);
+ if (orig_pktlen)
+ pktlen--;
+ if (version != 1)
+ {
+ log_error ("aead encrypted packet with unknown version %d\n",
+ version);
+ if (list_mode)
+ es_fputs (":aead encrypted packet: [unknown version]\n", listfp);
+ /*skip_rest(inp, pktlen); should we really do this? */
+ rc = gpg_error (GPG_ERR_INV_PACKET);
+ goto leave;
+ }
+
+ ed->cipher_algo = iobuf_get_noeof (inp);
+ if (orig_pktlen)
+ pktlen--;
+ ed->aead_algo = iobuf_get_noeof (inp);
+ if (orig_pktlen)
+ pktlen--;
+ ed->chunkbyte = iobuf_get_noeof (inp);
+ if (orig_pktlen)
+ pktlen--;
+
+ /* Store the remaining length of the encrypted data. We read the
+ * rest during decryption. */
+ ed->len = pktlen;
+
+ if (list_mode)
+ {
+ es_fprintf (listfp, ":aead encrypted packet: cipher=%u aead=%u cb=%u\n",
+ ed->cipher_algo, ed->aead_algo, ed->chunkbyte);
+ if (orig_pktlen)
+ es_fprintf (listfp, "\tlength: %lu\n", orig_pktlen);
+ else
+ es_fprintf (listfp, "\tlength: unknown\n");
+ }
+
+ ed->buf = inp;
+
+ leave:
+ return rc;
+}
+
+
/*
- * This packet is internally generated by us (ibn armor.c) to transfer
+ * This packet is internally generated by us (in armor.c) to transfer
* some information to the lower layer. To make sure that this packet
- * is really a GPG faked one and not one comming from outside, we
+ * is really a GPG faked one and not one coming from outside, we
* first check that there is a unique tag in it.
*
* The format of such a control packet is:
goto skipit; /* Definitely too large. We skip it to avoid an
overflow in the malloc. */
if (list_mode)
- puts ("- gpg control packet");
+ es_fputs ("- gpg control packet", listfp);
packet->pkt.gpg_control = xmalloc (sizeof *packet->pkt.gpg_control
+ pktlen - 1);