summaryrefslogtreecommitdiffstats
path: root/g10/parse-packet.c
diff options
context:
space:
mode:
authorRepo Admin <nobody@gnupg.org>2003-06-05 09:14:21 +0200
committerRepo Admin <nobody@gnupg.org>2003-06-05 09:14:21 +0200
commit7250331472efe70fac928fa06e51c7c80f2b715c (patch)
treed6ec62958ba3971115da3b81c8e1ad0f6d822fe0 /g10/parse-packet.c
parentMake use of libgpg-error (diff)
downloadgnupg2-7250331472efe70fac928fa06e51c7c80f2b715c.tar.xz
gnupg2-7250331472efe70fac928fa06e51c7c80f2b715c.zip
This commit was manufactured by cvs2svn to create branch
'GNUPG-1-9-BRANCH'.
Diffstat (limited to 'g10/parse-packet.c')
-rw-r--r--g10/parse-packet.c2281
1 files changed, 2281 insertions, 0 deletions
diff --git a/g10/parse-packet.c b/g10/parse-packet.c
new file mode 100644
index 000000000..a881840b2
--- /dev/null
+++ b/g10/parse-packet.c
@@ -0,0 +1,2281 @@
+/* parse-packet.c - read packets
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ * 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "packet.h"
+#include "iobuf.h"
+#include "mpi.h"
+#include "util.h"
+#include "cipher.h"
+#include "memory.h"
+#include "filter.h"
+#include "photoid.h"
+#include "options.h"
+#include "main.h"
+#include "i18n.h"
+
+static int mpi_print_mode = 0;
+static int list_mode = 0;
+
+static int parse( IOBUF inp, PACKET *pkt, int onlykeypkts,
+ off_t *retpos, int *skip, IOBUF out, int do_skip
+#ifdef DEBUG_PARSE_PACKET
+ ,const char *dbg_w, const char *dbg_f, int dbg_l
+#endif
+ );
+static int copy_packet( IOBUF inp, IOBUF out, int pkttype,
+ unsigned long pktlen );
+static void skip_packet( IOBUF inp, int pkttype, unsigned long pktlen );
+static void skip_rest( IOBUF inp, unsigned long pktlen );
+static void *read_rest( IOBUF inp, size_t pktlen );
+static int parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet );
+static int parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet );
+static int parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
+ PKT_signature *sig );
+static int parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen,
+ PKT_onepass_sig *ops );
+static int parse_key( IOBUF inp, int pkttype, unsigned long pktlen,
+ byte *hdr, int hdrlen, PACKET *packet );
+static int parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet );
+static int parse_attribute( IOBUF inp, int pkttype, unsigned long pktlen,
+ 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 int parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet, int new_ctb);
+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);
+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 );
+
+static unsigned short
+read_16(IOBUF inp)
+{
+ unsigned short a;
+ a = iobuf_get_noeof(inp) << 8;
+ a |= iobuf_get_noeof(inp);
+ return a;
+}
+
+static unsigned long
+read_32(IOBUF inp)
+{
+ unsigned long a;
+ a = iobuf_get_noeof(inp) << 24;
+ a |= iobuf_get_noeof(inp) << 16;
+ a |= iobuf_get_noeof(inp) << 8;
+ a |= iobuf_get_noeof(inp);
+ return a;
+}
+
+
+int
+set_packet_list_mode( int mode )
+{
+ int old = list_mode;
+ list_mode = mode;
+ mpi_print_mode = DBG_MPI;
+ return old;
+}
+
+static void
+unknown_pubkey_warning( int algo )
+{
+ static byte unknown_pubkey_algos[256];
+
+ algo &= 0xff;
+ if( !unknown_pubkey_algos[algo] ) {
+ if( opt.verbose )
+ log_info(_("can't handle public key algorithm %d\n"), algo );
+ unknown_pubkey_algos[algo] = 1;
+ }
+}
+
+/****************
+ * Parse a Packet and return it in packet
+ * 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
+int
+dbg_parse_packet( IOBUF inp, 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 );
+ } while( skip );
+ return rc;
+}
+#else
+int
+parse_packet( IOBUF inp, PACKET *pkt )
+{
+ int skip, rc;
+
+ do {
+ rc = parse( inp, pkt, 0, NULL, &skip, NULL, 0 );
+ } while( skip );
+ return rc;
+}
+#endif
+
+/****************
+ * Like parse packet, but only return secret or public (sub)key packets.
+ */
+#ifdef DEBUG_PARSE_PACKET
+int
+dbg_search_packet( IOBUF inp, 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 );
+ } while( skip );
+ return rc;
+}
+#else
+int
+search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid )
+{
+ int skip, rc;
+
+ do {
+ rc = parse( inp, pkt, with_uid?2:1, retpos, &skip, NULL, 0 );
+ } while( skip );
+ return rc;
+}
+#endif
+
+/****************
+ * Copy all packets from INP to OUT, thereby removing unused spaces.
+ */
+#ifdef DEBUG_PARSE_PACKET
+int
+dbg_copy_all_packets( IOBUF inp, IOBUF out,
+ const char *dbg_f, int dbg_l )
+{
+ PACKET pkt;
+ int skip, rc=0;
+ do {
+ init_packet(&pkt);
+ } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0, "copy", dbg_f, dbg_l )));
+ return rc;
+}
+#else
+int
+copy_all_packets( IOBUF inp, IOBUF out )
+{
+ PACKET pkt;
+ int skip, rc=0;
+ do {
+ init_packet(&pkt);
+ } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )));
+ return rc;
+}
+#endif
+
+/****************
+ * Copy some packets from INP to OUT, thereby removing unused spaces.
+ * Stop at offset STOPoff (i.e. don't copy packets at this or later offsets)
+ */
+#ifdef DEBUG_PARSE_PACKET
+int
+dbg_copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff,
+ const char *dbg_f, int dbg_l )
+{
+ PACKET pkt;
+ int skip, rc=0;
+ do {
+ if( iobuf_tell(inp) >= stopoff )
+ return 0;
+ init_packet(&pkt);
+ } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0,
+ "some", dbg_f, dbg_l )) );
+ return rc;
+}
+#else
+int
+copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff )
+{
+ PACKET pkt;
+ int skip, rc=0;
+ do {
+ if( iobuf_tell(inp) >= stopoff )
+ return 0;
+ init_packet(&pkt);
+ } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )) );
+ return rc;
+}
+#endif
+
+/****************
+ * Skip over N packets
+ */
+#ifdef DEBUG_PARSE_PACKET
+int
+dbg_skip_some_packets( IOBUF inp, unsigned n,
+ const char *dbg_f, int dbg_l )
+{
+ int skip, rc=0;
+ PACKET pkt;
+
+ for( ;n && !rc; n--) {
+ init_packet(&pkt);
+ rc = parse( inp, &pkt, 0, NULL, &skip, NULL, 1, "skip", dbg_f, dbg_l );
+ }
+ return rc;
+}
+#else
+int
+skip_some_packets( IOBUF inp, unsigned n )
+{
+ int skip, rc=0;
+ PACKET pkt;
+
+ for( ;n && !rc; n--) {
+ init_packet(&pkt);
+ rc = parse( inp, &pkt, 0, NULL, &skip, NULL, 1 );
+ }
+ return rc;
+}
+#endif
+
+
+/****************
+ * Parse packet. Set the variable skip points to 1 if the packet
+ * should be skipped; this is the case if either ONLYKEYPKTS is set
+ * and the parsed packet isn't one or the
+ * packet-type is 0, indicating deleted stuff.
+ * if OUT is not NULL, a special copymode is used.
+ */
+static int
+parse( IOBUF inp, PACKET *pkt, int onlykeypkts, off_t *retpos,
+ int *skip, IOBUF out, int do_skip
+#ifdef DEBUG_PARSE_PACKET
+ ,const char *dbg_w, const char *dbg_f, int dbg_l
+#endif
+ )
+{
+ int rc=0, c, ctb, pkttype, lenbytes;
+ unsigned long pktlen;
+ byte hdr[8];
+ int hdrlen;
+ int new_ctb = 0;
+ int with_uid = (onlykeypkts == 2);
+
+ *skip = 0;
+ assert( !pkt->pkt.generic );
+ if( retpos )
+ *retpos = iobuf_tell(inp);
+
+ if( (ctb = iobuf_get(inp)) == -1 ) {
+ rc = -1;
+ goto leave;
+ }
+ hdrlen=0;
+ hdr[hdrlen++] = ctb;
+ if( !(ctb & 0x80) ) {
+ log_error("%s: invalid packet (ctb=%02x)\n", iobuf_where(inp), ctb );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ pktlen = 0;
+ new_ctb = !!(ctb & 0x40);
+ if( new_ctb ) {
+ pkttype = ctb & 0x3f;
+ if( (c = iobuf_get(inp)) == -1 ) {
+ log_error("%s: 1st length byte missing\n", iobuf_where(inp) );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ if (pkttype == PKT_COMPRESSED) {
+ iobuf_set_partial_block_mode(inp, c & 0xff);
+ pktlen = 0;/* to indicate partial length */
+ }
+ else {
+ hdr[hdrlen++] = c;
+ if( c < 192 )
+ pktlen = c;
+ else if( c < 224 ) {
+ pktlen = (c - 192) * 256;
+ if( (c = iobuf_get(inp)) == -1 ) {
+ log_error("%s: 2nd length byte missing\n",
+ iobuf_where(inp) );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ hdr[hdrlen++] = c;
+ pktlen += c + 192;
+ }
+ 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 ) {
+ log_error("%s: 4 byte length invalid\n",
+ iobuf_where(inp) );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ pktlen |= (hdr[hdrlen++] = c );
+ }
+ else { /* partial body length */
+ iobuf_set_partial_block_mode(inp, c & 0xff);
+ pktlen = 0;/* to indicate partial length */
+ }
+ }
+ }
+ else {
+ pkttype = (ctb>>2)&0xf;
+ lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
+ if( !lenbytes ) {
+ pktlen = 0; /* don't know the value */
+ if( pkttype != PKT_COMPRESSED )
+ iobuf_set_block_mode(inp, 1);
+ }
+ else {
+ for( ; lenbytes; lenbytes-- ) {
+ pktlen <<= 8;
+ pktlen |= hdr[hdrlen++] = iobuf_get_noeof(inp);
+ }
+ }
+ }
+
+ if (pktlen == 0xffffffff) {
+ /* with a some probability this is caused by a problem in the
+ * the uncompressing layer - in some error cases it just loops
+ * and spits out 0xff bytes. */
+ log_error ("%s: garbled packet detected\n", iobuf_where(inp) );
+ g10_exit (2);
+ }
+
+ if( out && pkttype ) {
+ if( iobuf_write( out, hdr, hdrlen ) == -1 )
+ rc = G10ERR_WRITE_FILE;
+ else
+ rc = copy_packet(inp, out, pkttype, pktlen );
+ goto leave;
+ }
+
+ if (with_uid && pkttype == PKT_USER_ID)
+ ;
+ else if( do_skip
+ || !pkttype
+ || (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY
+ && pkttype != PKT_PUBLIC_KEY
+ && pkttype != PKT_SECRET_SUBKEY
+ && pkttype != PKT_SECRET_KEY ) ) {
+ skip_rest(inp, pktlen);
+ *skip = 1;
+ rc = 0;
+ goto leave;
+ }
+
+ if( DBG_PACKET ) {
+#ifdef 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 );
+#else
+ log_debug("parse_packet(iob=%d): type=%d length=%lu%s\n",
+ iobuf_id(inp), pkttype, pktlen, new_ctb?" (new_ctb)":"" );
+#endif
+ }
+ pkt->pkttype = pkttype;
+ rc = G10ERR_UNKNOWN_PACKET; /* default error */
+ switch( pkttype ) {
+ case PKT_PUBLIC_KEY:
+ case PKT_PUBLIC_SUBKEY:
+ pkt->pkt.public_key = m_alloc_clear(sizeof *pkt->pkt.public_key );
+ rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt );
+ break;
+ case PKT_SECRET_KEY:
+ case PKT_SECRET_SUBKEY:
+ pkt->pkt.secret_key = m_alloc_clear(sizeof *pkt->pkt.secret_key );
+ rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt );
+ break;
+ case PKT_SYMKEY_ENC:
+ rc = parse_symkeyenc( inp, pkttype, pktlen, pkt );
+ break;
+ case PKT_PUBKEY_ENC:
+ rc = parse_pubkeyenc(inp, pkttype, pktlen, pkt );
+ break;
+ case PKT_SIGNATURE:
+ pkt->pkt.signature = m_alloc_clear(sizeof *pkt->pkt.signature );
+ rc = parse_signature(inp, pkttype, pktlen, pkt->pkt.signature );
+ break;
+ case PKT_ONEPASS_SIG:
+ pkt->pkt.onepass_sig = m_alloc_clear(sizeof *pkt->pkt.onepass_sig );
+ rc = parse_onepass_sig(inp, pkttype, pktlen, pkt->pkt.onepass_sig );
+ break;
+ case PKT_USER_ID:
+ rc = parse_user_id(inp, pkttype, pktlen, pkt );
+ break;
+ case PKT_ATTRIBUTE:
+ pkt->pkttype = pkttype = PKT_USER_ID; /* we store it in the userID */
+ rc = parse_attribute(inp, pkttype, pktlen, pkt);
+ break;
+ case PKT_OLD_COMMENT:
+ case PKT_COMMENT:
+ rc = parse_comment(inp, pkttype, pktlen, pkt);
+ break;
+ case PKT_RING_TRUST:
+ parse_trust(inp, pkttype, pktlen, pkt);
+ rc = 0;
+ break;
+ case PKT_PLAINTEXT:
+ rc = parse_plaintext(inp, pkttype, pktlen, pkt, new_ctb );
+ break;
+ case PKT_COMPRESSED:
+ rc = parse_compressed(inp, pkttype, pktlen, pkt, new_ctb );
+ break;
+ case PKT_ENCRYPTED:
+ case PKT_ENCRYPTED_MDC:
+ rc = parse_encrypted(inp, pkttype, pktlen, pkt, new_ctb );
+ break;
+ case PKT_MDC:
+ rc = parse_mdc(inp, pkttype, pktlen, pkt, new_ctb );
+ break;
+ case PKT_GPG_CONTROL:
+ rc = parse_gpg_control(inp, pkttype, pktlen, pkt );
+ break;
+ default:
+ skip_packet(inp, pkttype, pktlen);
+ break;
+ }
+
+ leave:
+ if( !rc && iobuf_error(inp) )
+ rc = G10ERR_INV_KEYRING;
+ return rc;
+}
+
+static void
+dump_hex_line( int c, int *i )
+{
+ if( *i && !(*i%8) ) {
+ if( *i && !(*i%24) )
+ printf("\n%4d:", *i );
+ else
+ putchar(' ');
+ }
+ if( c == -1 )
+ printf(" EOF" );
+ else
+ printf(" %02x", c );
+ ++*i;
+}
+
+
+static int
+copy_packet( IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen )
+{
+ int n;
+ char buf[100];
+
+ if( iobuf_in_block_mode(inp) ) {
+ while( (n = iobuf_read( inp, buf, 100 )) != -1 )
+ if( iobuf_write(out, buf, n ) )
+ return G10ERR_WRITE_FILE; /* write error */
+ }
+ else if( !pktlen && pkttype == PKT_COMPRESSED ) {
+ log_debug("copy_packet: compressed!\n");
+ /* compressed packet, copy till EOF */
+ while( (n = iobuf_read( inp, buf, 100 )) != -1 )
+ if( iobuf_write(out, buf, n ) )
+ return G10ERR_WRITE_FILE; /* write error */
+ }
+ else {
+ for( ; pktlen; pktlen -= n ) {
+ n = pktlen > 100 ? 100 : pktlen;
+ n = iobuf_read( inp, buf, n );
+ if( n == -1 )
+ return G10ERR_READ_FILE;
+ if( iobuf_write(out, buf, n ) )
+ return G10ERR_WRITE_FILE; /* write error */
+ }
+ }
+ return 0;
+}
+
+
+static void
+skip_packet( IOBUF inp, int pkttype, unsigned long pktlen )
+{
+ if( list_mode ) {
+ if( pkttype == PKT_MARKER )
+ fputs(":marker packet:\n", stdout );
+ else
+ printf(":unknown packet: type %2d, length %lu\n", pkttype, pktlen);
+ if( pkttype ) {
+ int c, i=0 ;
+ if( pkttype != PKT_MARKER )
+ fputs("dump:", stdout );
+ if( iobuf_in_block_mode(inp) ) {
+ while( (c=iobuf_get(inp)) != -1 )
+ dump_hex_line(c, &i);
+ }
+ else {
+ for( ; pktlen; pktlen-- )
+ dump_hex_line(iobuf_get(inp), &i);
+ }
+ putchar('\n');
+ return;
+ }
+ }
+ skip_rest(inp,pktlen);
+}
+
+static void
+skip_rest( IOBUF inp, unsigned long pktlen )
+{
+ if( iobuf_in_block_mode(inp) ) {
+ while( iobuf_get(inp) != -1 )
+ ;
+ }
+ else {
+ for( ; pktlen; pktlen-- )
+ if( iobuf_get(inp) == -1 )
+ break;
+ }
+}
+
+
+static void *
+read_rest( IOBUF inp, size_t pktlen )
+{
+ byte *p;
+ int i;
+
+ if( iobuf_in_block_mode(inp) ) {
+ log_error("read_rest: can't store stream data\n");
+ p = NULL;
+ }
+ else {
+ p = m_alloc( pktlen );
+ for(i=0; pktlen; pktlen--, i++ )
+ p[i] = iobuf_get(inp);
+ }
+ return p;
+}
+
+
+
+static int
+parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+ PKT_symkey_enc *k;
+ int rc = 0;
+ int i, version, s2kmode, cipher_algo, hash_algo, seskeylen, minlen;
+
+ if( pktlen < 4 ) {
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ version = iobuf_get_noeof(inp); pktlen--;
+ if( version != 4 ) {
+ log_error("packet(%d) with unknown version %d\n", pkttype, version);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ if( pktlen > 200 ) { /* (we encode the seskeylen in a byte) */
+ log_error("packet(%d) too large\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ cipher_algo = iobuf_get_noeof(inp); pktlen--;
+ s2kmode = iobuf_get_noeof(inp); pktlen--;
+ hash_algo = iobuf_get_noeof(inp); pktlen--;
+ switch( s2kmode ) {
+ case 0: /* simple s2k */
+ minlen = 0;
+ break;
+ case 1: /* salted s2k */
+ minlen = 8;
+ break;
+ case 3: /* iterated+salted s2k */
+ minlen = 9;
+ break;
+ default:
+ log_error("unknown S2K %d\n", s2kmode );
+ goto leave;
+ }
+ if( minlen > pktlen ) {
+ log_error("packet with S2K %d too short\n", s2kmode );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ seskeylen = pktlen - minlen;
+ k = packet->pkt.symkey_enc = m_alloc_clear( sizeof *packet->pkt.symkey_enc
+ + seskeylen - 1 );
+ k->version = version;
+ k->cipher_algo = cipher_algo;
+ k->s2k.mode = s2kmode;
+ k->s2k.hash_algo = hash_algo;
+ if( s2kmode == 1 || s2kmode == 3 ) {
+ for(i=0; i < 8 && pktlen; i++, pktlen-- )
+ k->s2k.salt[i] = iobuf_get_noeof(inp);
+ }
+ if( s2kmode == 3 ) {
+ k->s2k.count = iobuf_get(inp); pktlen--;
+ }
+ k->seskeylen = seskeylen;
+ for(i=0; i < seskeylen && pktlen; i++, pktlen-- )
+ k->seskey[i] = iobuf_get_noeof(inp);
+ assert( !pktlen );
+
+ if( list_mode ) {
+ printf(":symkey enc packet: version %d, cipher %d, s2k %d, hash %d\n",
+ version, cipher_algo, s2kmode, hash_algo);
+ if( s2kmode == 1 || s2kmode == 3 ) {
+ printf("\tsalt ");
+ for(i=0; i < 8; i++ )
+ printf("%02x", k->s2k.salt[i]);
+ if( s2kmode == 3 )
+ printf(", count %lu\n", (ulong)k->s2k.count );
+ printf("\n");
+ }
+ }
+
+ leave:
+ skip_rest(inp, pktlen);
+ return rc;
+}
+
+static int
+parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+ unsigned int n;
+ int rc = 0;
+ int i, ndata;
+ PKT_pubkey_enc *k;
+
+ k = packet->pkt.pubkey_enc = m_alloc_clear(sizeof *packet->pkt.pubkey_enc);
+ if( pktlen < 12 ) {
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ k->version = iobuf_get_noeof(inp); pktlen--;
+ if( k->version != 2 && k->version != 3 ) {
+ log_error("packet(%d) with unknown version %d\n", pkttype, k->version);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ k->keyid[0] = read_32(inp); pktlen -= 4;
+ k->keyid[1] = read_32(inp); pktlen -= 4;
+ k->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
+ k->throw_keyid = 0; /* only used as flag for build_packet */
+ if( list_mode )
+ printf(":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n",
+ k->version, k->pubkey_algo, (ulong)k->keyid[0], (ulong)k->keyid[1]);
+
+ ndata = pubkey_get_nenc(k->pubkey_algo);
+ if( !ndata ) {
+ if( list_mode )
+ printf("\tunsupported algorithm %d\n", k->pubkey_algo );
+ unknown_pubkey_warning( k->pubkey_algo );
+ k->data[0] = NULL; /* no need to store the encrypted data */
+ }
+ else {
+ for( i=0; i < ndata; i++ ) {
+ n = pktlen;
+ k->data[i] = mpi_read(inp, &n, 0); pktlen -=n;
+ if( list_mode ) {
+ printf("\tdata: ");
+ mpi_print(stdout, k->data[i], mpi_print_mode );
+ putchar('\n');
+ }
+ if (!k->data[i])
+ rc = G10ERR_INVALID_PACKET;
+ }
+ }
+
+ leave:
+ skip_rest(inp, pktlen);
+ return rc;
+}
+
+
+static void
+dump_sig_subpkt( int hashed, int type, int critical,
+ const byte *buffer, size_t buflen, size_t length )
+{
+ const char *p=NULL;
+ int i;
+
+ /* The CERT has warning out with explains how to use GNUPG to
+ * detect the ARRs - we print our old message here when it is a faked
+ * ARR and add an additional notice */
+ if ( type == SIGSUBPKT_ARR && !hashed ) {
+ printf("\tsubpkt %d len %u (additional recipient request)\n"
+ "WARNING: PGP versions > 5.0 and < 6.5.8 will automagically "
+ "encrypt to this key and thereby reveal the plaintext to "
+ "the owner of this ARR key. Detailed info follows:\n",
+ type, (unsigned)length );
+ }
+
+ buffer++;
+ length--;
+
+ printf("\t%s%ssubpkt %d len %u (", /*)*/
+ critical ? "critical ":"",
+ hashed ? "hashed ":"", type, (unsigned)length );
+ if( length > buflen ) {
+ printf("too short: buffer is only %u)\n", (unsigned)buflen );
+ return;
+ }
+ switch( type ) {
+ case SIGSUBPKT_SIG_CREATED:
+ if( length >= 4 )
+ printf("sig created %s", strtimestamp( buffer_to_u32(buffer) ) );
+ break;
+ case SIGSUBPKT_SIG_EXPIRE:
+ if( length >= 4 )
+ printf("sig expires after %s",
+ strtimevalue( buffer_to_u32(buffer) ) );
+ break;
+ case SIGSUBPKT_EXPORTABLE:
+ if( length )
+ printf("%sexportable", *buffer? "":"not ");
+ break;
+ case SIGSUBPKT_TRUST:
+ if(length!=2)
+ p="[invalid trust subpacket]";
+ else
+ printf("trust signature of depth %d, value %d",buffer[0],buffer[1]);
+ break;
+ case SIGSUBPKT_REGEXP:
+ if(!length)
+ p="[invalid regexp subpacket]";
+ else
+ printf("regular expression: \"%s\"",buffer);
+ break;
+ case SIGSUBPKT_REVOCABLE:
+ if( length )
+ printf("%srevocable", *buffer? "":"not ");
+ break;
+ case SIGSUBPKT_KEY_EXPIRE:
+ if( length >= 4 )
+ printf("key expires after %s",
+ strtimevalue( buffer_to_u32(buffer) ) );
+ break;
+ case SIGSUBPKT_PREF_SYM:
+ fputs("pref-sym-algos:", stdout );
+ for( i=0; i < length; i++ )
+ printf(" %d", buffer[i] );
+ break;
+ case SIGSUBPKT_REV_KEY:
+ fputs("revocation key: ", stdout );
+ if( length < 22 )
+ p = "[too short]";
+ else {
+ printf("c=%02x a=%d f=", buffer[0], buffer[1] );
+ for( i=2; i < length; i++ )
+ printf("%02X", buffer[i] );
+ }
+ break;
+ case SIGSUBPKT_ISSUER:
+ if( length >= 8 )
+ printf("issuer key ID %08lX%08lX",
+ (ulong)buffer_to_u32(buffer),
+ (ulong)buffer_to_u32(buffer+4) );
+ break;
+ case SIGSUBPKT_NOTATION:
+ {
+ fputs("notation: ", stdout );
+ if( length < 8 )
+ p = "[too short]";
+ else {
+ const byte *s = buffer;
+ size_t n1, n2;
+
+ n1 = (s[4] << 8) | s[5];
+ n2 = (s[6] << 8) | s[7];
+ s += 8;
+ if( 8+n1+n2 != length )
+ p = "[error]";
+ else {
+ print_string( stdout, s, n1, ')' );
+ putc( '=', stdout );
+
+ if( *buffer & 0x80 )
+ print_string( stdout, s+n1, n2, ')' );
+ else
+ p = "[not human readable]";
+ }
+ }
+ }
+ break;
+ case SIGSUBPKT_PREF_HASH:
+ fputs("pref-hash-algos:", stdout );
+ for( i=0; i < length; i++ )
+ printf(" %d", buffer[i] );
+ break;
+ case SIGSUBPKT_PREF_COMPR:
+ fputs("pref-zip-algos:", stdout );
+ for( i=0; i < length; i++ )
+ printf(" %d", buffer[i] );
+ break;
+ case SIGSUBPKT_KS_FLAGS:
+ fputs("key server preferences:",stdout);
+ for(i=0;i<length;i++)
+ printf(" %02X", buffer[i]);
+ break;
+ case SIGSUBPKT_PREF_KS:
+ p = "preferred key server";
+ break;
+ case SIGSUBPKT_PRIMARY_UID:
+ p = "primary user ID";
+ break;
+ case SIGSUBPKT_POLICY:
+ fputs("policy: ", stdout );
+ print_string( stdout, buffer, length, ')' );
+ break;
+ case SIGSUBPKT_KEY_FLAGS:
+ fputs ( "key flags:", stdout );
+ for( i=0; i < length; i++ )
+ printf(" %02X", buffer[i] );
+ break;
+ case SIGSUBPKT_SIGNERS_UID:
+ p = "signer's user ID";
+ break;
+ case SIGSUBPKT_REVOC_REASON:
+ if( length ) {
+ printf("revocation reason 0x%02x (", *buffer );
+ print_string( stdout, buffer+1, length-1, ')' );
+ p = ")";
+ }
+ break;
+ case SIGSUBPKT_ARR:
+ fputs("Big Brother's key (ignored): ", stdout );
+ if( length < 22 )
+ p = "[too short]";
+ else {
+ printf("c=%02x a=%d f=", buffer[0], buffer[1] );
+ for( i=2; i < length; i++ )
+ printf("%02X", buffer[i] );
+ }
+ break;
+ case SIGSUBPKT_FEATURES:
+ fputs ( "features:", stdout );
+ for( i=0; i < length; i++ )
+ printf(" %02x", buffer[i] );
+ break;
+ default:
+ if(type>=100 && type<=110)
+ p="experimental / private subpacket";
+ else
+ p = "?";
+ break;
+ }
+
+ printf("%s)\n", p? p: "");
+}
+
+/****************
+ * Returns: >= 0 offset into buffer
+ * -1 unknown type
+ * -2 unsupported type
+ * -3 subpacket too short
+ */
+int
+parse_one_sig_subpkt( const byte *buffer, size_t n, int type )
+{
+ switch( type ) {
+ case SIGSUBPKT_REV_KEY:
+ if(n < 22)
+ break;
+ return 0;
+ case SIGSUBPKT_SIG_CREATED:
+ case SIGSUBPKT_SIG_EXPIRE:
+ case SIGSUBPKT_KEY_EXPIRE:
+ if( n < 4 )
+ break;
+ return 0;
+ case SIGSUBPKT_KEY_FLAGS:
+ case SIGSUBPKT_KS_FLAGS:
+ case SIGSUBPKT_PREF_SYM:
+ case SIGSUBPKT_PREF_HASH:
+ case SIGSUBPKT_PREF_COMPR:
+ case SIGSUBPKT_POLICY:
+ case SIGSUBPKT_FEATURES:
+ case SIGSUBPKT_REGEXP:
+ return 0;
+ case SIGSUBPKT_EXPORTABLE:
+ case SIGSUBPKT_REVOCABLE:
+ if( !n )
+ break;
+ return 0;
+ case SIGSUBPKT_ISSUER: /* issuer key ID */
+ if( n < 8 )
+ break;
+ return 0;
+ case SIGSUBPKT_NOTATION:
+ if( n < 8 ) /* minimum length needed */
+ break;
+ return 0;
+ case SIGSUBPKT_REVOC_REASON:
+ if( !n )
+ break;
+ return 0;
+ case SIGSUBPKT_PRIMARY_UID:
+ if ( n != 1 )
+ break;
+ return 0;
+ case SIGSUBPKT_TRUST:
+ if ( n != 2 )
+ break;
+ return 0;
+ default: return -1;
+ }
+ return -3;
+}
+
+
+static int
+can_handle_critical( const byte *buffer, size_t n, int type )
+{
+ switch( type ) {
+ case SIGSUBPKT_NOTATION:
+ if( n >= 8 && (*buffer & 0x80) )
+ return 1; /* human readable is handled */
+ return 0;
+
+ case SIGSUBPKT_SIG_CREATED:
+ case SIGSUBPKT_SIG_EXPIRE:
+ case SIGSUBPKT_KEY_EXPIRE:
+ case SIGSUBPKT_EXPORTABLE:
+ case SIGSUBPKT_REVOCABLE:
+ case SIGSUBPKT_REV_KEY:
+ case SIGSUBPKT_ISSUER:/* issuer key ID */
+ case SIGSUBPKT_PREF_SYM:
+ case SIGSUBPKT_PREF_HASH:
+ case SIGSUBPKT_PREF_COMPR:
+ case SIGSUBPKT_KEY_FLAGS:
+ case SIGSUBPKT_PRIMARY_UID:
+ case SIGSUBPKT_FEATURES:
+ case SIGSUBPKT_POLICY: /* Is it enough to show the policy? */
+ case SIGSUBPKT_TRUST:
+ case SIGSUBPKT_REGEXP:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+const byte *
+enum_sig_subpkt( const subpktarea_t *pktbuf, sigsubpkttype_t reqtype,
+ size_t *ret_n, int *start, int *critical )
+{
+ const byte *buffer;
+ int buflen;
+ int type;
+ int critical_dummy;
+ int offset;
+ size_t n;
+ int seq = 0;
+ int reqseq = start? *start: 0;
+
+ if(!critical)
+ critical=&critical_dummy;
+
+ if( !pktbuf || reqseq == -1 ) {
+ /* return some value different from NULL to indicate that
+ * there is no critical bit we do not understand. The caller
+ * will never use the value. Yes I know, it is an ugly hack */
+ return reqtype == SIGSUBPKT_TEST_CRITICAL? (const byte*)&pktbuf : NULL;
+ }
+ buffer = pktbuf->data;
+ buflen = pktbuf->len;
+ while( buflen ) {
+ n = *buffer++; buflen--;
+ if( n == 255 ) { /* 4 byte length header */
+ if( buflen < 4 )
+ goto too_short;
+ n = (buffer[0] << 24) | (buffer[1] << 16)
+ | (buffer[2] << 8) | buffer[3];
+ buffer += 4;
+ buflen -= 4;
+ }
+ else if( n >= 192 ) { /* 2 byte special encoded length header */
+ if( buflen < 2 )
+ goto too_short;
+ n = (( n - 192 ) << 8) + *buffer + 192;
+ buffer++;
+ buflen--;
+ }
+ if( buflen < n )
+ goto too_short;
+ type = *buffer;
+ if( type & 0x80 ) {
+ type &= 0x7f;
+ *critical = 1;
+ }
+ else
+ *critical = 0;
+ if( !(++seq > reqseq) )
+ ;
+ else if( reqtype == SIGSUBPKT_TEST_CRITICAL ) {
+ if( *critical ) {
+ if( n-1 > buflen+1 )
+ goto too_short;
+ if( !can_handle_critical(buffer+1, n-1, type ) )
+ {
+ if(opt.verbose)
+ log_info(_("subpacket of type %d has "
+ "critical bit set\n"),type);
+ if( start )
+ *start = seq;
+ return NULL; /* this is an error */
+ }
+ }
+ }
+ else if( reqtype < 0 ) /* list packets */
+ dump_sig_subpkt( reqtype == SIGSUBPKT_LIST_HASHED,
+ type, *critical, buffer, buflen, n );
+ else if( type == reqtype ) { /* found */
+ buffer++;
+ n--;
+ if( n > buflen )
+ goto too_short;
+ if( ret_n )
+ *ret_n = n;
+ offset = parse_one_sig_subpkt(buffer, n, type );
+ switch( offset ) {
+ case -3:
+ log_error("subpacket of type %d too short\n", type);
+ return NULL;
+ case -2:
+ return NULL;
+ case -1:
+ BUG(); /* not yet needed */
+ default:
+ break;
+ }
+ if( start )
+ *start = seq;
+ return buffer+offset;
+ }
+ buffer += n; buflen -=n;
+ }
+ if( reqtype == SIGSUBPKT_TEST_CRITICAL )
+ return buffer; /* as value true to indicate that there is no */
+ /* critical bit we don't understand */
+ if( start )
+ *start = -1;
+ return NULL; /* end of packets; not found */
+
+ too_short:
+ log_error("buffer shorter than subpacket\n");
+ if( start )
+ *start = -1;
+ return NULL;
+}
+
+
+const byte *
+parse_sig_subpkt (const subpktarea_t *buffer, sigsubpkttype_t reqtype,
+ size_t *ret_n)
+{
+ return enum_sig_subpkt( buffer, reqtype, ret_n, NULL, NULL );
+}
+
+const byte *
+parse_sig_subpkt2 (PKT_signature *sig, sigsubpkttype_t reqtype,
+ size_t *ret_n )
+{
+ const byte *p;
+
+ p = parse_sig_subpkt (sig->hashed, reqtype, ret_n );
+ if( !p )
+ p = parse_sig_subpkt (sig->unhashed, reqtype, ret_n );
+ return p;
+}
+
+/* Find all revocation keys. Look in hashed area only. */
+void parse_revkeys(PKT_signature *sig)
+{
+ struct revocation_key *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)))
+ {
+ if(len==sizeof(struct revocation_key) &&
+ (revkey->class&0x80)) /* 0x80 bit must be set */
+ {
+ sig->revkey=m_realloc(sig->revkey,
+ sizeof(struct revocation_key *)*(sig->numrevkeys+1));
+ sig->revkey[sig->numrevkeys]=revkey;
+ sig->numrevkeys++;
+ }
+ }
+}
+
+static int
+parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
+ PKT_signature *sig )
+{
+ int md5_len=0;
+ unsigned n;
+ int is_v4=0;
+ int rc=0;
+ int i, ndata;
+
+ if( pktlen < 16 ) {
+ log_error("packet(%d) too short\n", pkttype);
+ goto leave;
+ }
+ sig->version = iobuf_get_noeof(inp); pktlen--;
+ if( sig->version == 4 )
+ is_v4=1;
+ else if( sig->version != 2 && sig->version != 3 ) {
+ log_error("packet(%d) with unknown version %d\n", pkttype, sig->version);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+
+ if( !is_v4 ) {
+ md5_len = iobuf_get_noeof(inp); pktlen--;
+ }
+ sig->sig_class = iobuf_get_noeof(inp); pktlen--;
+ if( !is_v4 ) {
+ sig->timestamp = read_32(inp); pktlen -= 4;
+ sig->keyid[0] = read_32(inp); pktlen -= 4;
+ sig->keyid[1] = read_32(inp); pktlen -= 4;
+ }
+ 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 */
+ n = read_16(inp); pktlen -= 2; /* length of hashed data */
+ if( n > 10000 ) {
+ log_error("signature packet: hashed data too long\n");
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ if( n ) {
+ sig->hashed = m_alloc (sizeof (*sig->hashed) + n - 1 );
+ sig->hashed->size = n;
+ sig->hashed->len = n;
+ if( iobuf_read (inp, sig->hashed->data, n ) != n ) {
+ log_error ("premature eof while reading "
+ "hashed signature data\n");
+ rc = -1;
+ goto leave;
+ }
+ pktlen -= n;
+ }
+ n = read_16(inp); pktlen -= 2; /* length of unhashed data */
+ if( n > 10000 ) {
+ log_error("signature packet: unhashed data too long\n");
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ if( n ) {
+ /* we add 8 extra bytes so that we have space for the signature
+ * status cache. Well we are wasting this if there is a cache
+ * packet already, but in the other case it avoids an realloc */
+ sig->unhashed = m_alloc (sizeof(*sig->unhashed) + n + 8 - 1 );
+ sig->unhashed->size = n + 8;
+ sig->unhashed->len = n;
+ if( iobuf_read(inp, sig->unhashed->data, n ) != n ) {
+ log_error("premature eof while reading "
+ "unhashed signature data\n");
+ rc = -1;
+ goto leave;
+ }
+ pktlen -= n;
+ }
+ }
+
+ if( pktlen < 5 ) { /* sanity check */
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+
+ 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 */
+ const byte *p;
+ size_t len;
+
+ /* set sig->flags.unknown_critical if there is a
+ * critical bit set for packets which we do not understand */
+ if( !parse_sig_subpkt (sig->hashed, SIGSUBPKT_TEST_CRITICAL, NULL)
+ || !parse_sig_subpkt (sig->unhashed, SIGSUBPKT_TEST_CRITICAL,
+ NULL) )
+ {
+ sig->flags.unknown_critical = 1;
+ }
+
+ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_CREATED, NULL );
+ if( !p )
+ log_error("signature packet without timestamp\n");
+ else
+ sig->timestamp = buffer_to_u32(p);
+ p = parse_sig_subpkt2( sig, SIGSUBPKT_ISSUER, NULL );
+ if( !p )
+ log_error("signature packet without keyid\n");
+ else {
+ sig->keyid[0] = buffer_to_u32(p);
+ sig->keyid[1] = buffer_to_u32(p+4);
+ }
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_SIG_EXPIRE,NULL);
+ if(p)
+ sig->expiredate=sig->timestamp+buffer_to_u32(p);
+ if(sig->expiredate && sig->expiredate<=make_timestamp())
+ sig->flags.expired=1;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,NULL);
+ if(p)
+ sig->flags.policy_url=1;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,NULL);
+ if(p)
+ sig->flags.notation=1;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_REVOCABLE,NULL);
+ if(p && *p==0)
+ sig->flags.revocable=0;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_TRUST,&len);
+ if(p && len==2)
+ {
+ sig->trust_depth=p[0];
+ sig->trust_value=p[1];
+
+ /* Only look for a regexp if there is also a trust
+ subpacket. */
+ sig->trust_regexp=
+ parse_sig_subpkt(sig->hashed,SIGSUBPKT_REGEXP,&len);
+
+ /* If the regular expression is of 0 length, there is no
+ regular expression. */
+ if(len==0)
+ sig->trust_regexp=NULL;
+ }
+
+ /* We accept the exportable subpacket from either the hashed
+ or unhashed areas as older versions of gpg put it in the
+ unhashed area. In theory, anyway, we should never see this
+ packet off of a local keyring. */
+
+ p=parse_sig_subpkt2(sig,SIGSUBPKT_EXPORTABLE,NULL);
+ if(p && *p==0)
+ sig->flags.exportable=0;
+
+ /* Find all revocation keys. */
+ if(sig->sig_class==0x1F)
+ parse_revkeys(sig);
+ }
+
+ if( list_mode ) {
+ printf(":signature packet: algo %d, keyid %08lX%08lX\n"
+ "\tversion %d, created %lu, md5len %d, sigclass %02x\n"
+ "\tdigest algo %d, begin of digest %02x %02x\n",
+ sig->pubkey_algo,
+ (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 ) {
+ parse_sig_subpkt (sig->hashed, SIGSUBPKT_LIST_HASHED, NULL );
+ parse_sig_subpkt (sig->unhashed, SIGSUBPKT_LIST_UNHASHED, NULL);
+ }
+ }
+
+ ndata = pubkey_get_nsig(sig->pubkey_algo);
+ if( !ndata ) {
+ if( list_mode )
+ printf("\tunknown algorithm %d\n", sig->pubkey_algo );
+ unknown_pubkey_warning( sig->pubkey_algo );
+ /* we store the plain material in data[0], so that we are able
+ * to write it back with build_packet() */
+ sig->data[0] = mpi_set_opaque(NULL, read_rest(inp, pktlen), pktlen );
+ pktlen = 0;
+ }
+ else {
+ for( i=0; i < ndata; i++ ) {
+ n = pktlen;
+ sig->data[i] = mpi_read(inp, &n, 0 );
+ pktlen -=n;
+ if( list_mode ) {
+ printf("\tdata: ");
+ mpi_print(stdout, sig->data[i], mpi_print_mode );
+ putchar('\n');
+ }
+ if (!sig->data[i])
+ rc = G10ERR_INVALID_PACKET;
+ }
+ }
+
+ leave:
+ skip_rest(inp, pktlen);
+ return rc;
+}
+
+
+static int
+parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen,
+ PKT_onepass_sig *ops )
+{
+ int version;
+ int rc = 0;
+
+ if( pktlen < 13 ) {
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ version = iobuf_get_noeof(inp); pktlen--;
+ if( version != 3 ) {
+ log_error("onepass_sig with unknown version %d\n", version);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ ops->sig_class = iobuf_get_noeof(inp); pktlen--;
+ ops->digest_algo = iobuf_get_noeof(inp); pktlen--;
+ ops->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
+ ops->keyid[0] = read_32(inp); pktlen -= 4;
+ ops->keyid[1] = read_32(inp); pktlen -= 4;
+ ops->last = iobuf_get_noeof(inp); pktlen--;
+ if( list_mode )
+ printf(":onepass_sig packet: keyid %08lX%08lX\n"
+ "\tversion %d, sigclass %02x, digest %d, pubkey %d, last=%d\n",
+ (ulong)ops->keyid[0], (ulong)ops->keyid[1],
+ version, ops->sig_class,
+ ops->digest_algo, ops->pubkey_algo, ops->last );
+
+
+ leave:
+ skip_rest(inp, pktlen);
+ return rc;
+}
+
+
+static MPI
+read_protected_v3_mpi (IOBUF inp, unsigned long *length)
+{
+ int c;
+ unsigned int nbits, nbytes;
+ unsigned char *buf, *p;
+ MPI val;
+
+ if (*length < 2)
+ {
+ log_error ("mpi too small\n");
+ return NULL;
+ }
+
+ if ((c=iobuf_get (inp)) == -1)
+ return NULL;
+ --*length;
+ nbits = c << 8;
+ if ((c=iobuf_get(inp)) == -1)
+ return NULL;
+ --*length;
+ nbits |= c;
+
+ if (nbits > 16384)
+ {
+ log_error ("mpi too large (%u bits)\n", nbits);
+ return NULL;
+ }
+ nbytes = (nbits+7) / 8;
+ buf = p = m_alloc (2 + nbytes);
+ *p++ = nbits >> 8;
+ *p++ = nbits;
+ for (; nbytes && length; nbytes--, --*length)
+ *p++ = iobuf_get (inp);
+ if (nbytes)
+ {
+ log_error ("packet shorter tham mpi\n");
+ m_free (buf);
+ return NULL;
+ }
+
+ /* convert buffer into an opaque MPI */
+ val = mpi_set_opaque (NULL, buf, p-buf);
+ return val;
+}
+
+
+static int
+parse_key( IOBUF inp, int pkttype, unsigned long pktlen,
+ byte *hdr, int hdrlen, PACKET *pkt )
+{
+ int i, version, algorithm;
+ unsigned n;
+ unsigned long timestamp, expiredate, max_expiredate;
+ int npkey, nskey;
+ int is_v4=0;
+ int rc=0;
+
+ version = iobuf_get_noeof(inp); pktlen--;
+ if( pkttype == PKT_PUBLIC_SUBKEY && version == '#' ) {
+ /* early versions of G10 use old PGP comments packets;
+ * luckily all those comments are started by a hash */
+ if( list_mode ) {
+ printf(":rfc1991 comment packet: \"" );
+ for( ; pktlen; pktlen-- ) {
+ int c;
+ c = iobuf_get_noeof(inp);
+ if( c >= ' ' && c <= 'z' )
+ putchar(c);
+ else
+ printf("\\x%02x", c );
+ }
+ printf("\"\n");
+ }
+ skip_rest(inp, pktlen);
+ return 0;
+ }
+ else if( version == 4 )
+ is_v4=1;
+ else if( version != 2 && version != 3 ) {
+ log_error("packet(%d) with unknown version %d\n", pkttype, version);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+
+ if( pktlen < 11 ) {
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+
+ timestamp = read_32(inp); pktlen -= 4;
+ if( is_v4 ) {
+ expiredate = 0; /* have to get it from the selfsignature */
+ max_expiredate = 0;
+ }
+ else {
+ unsigned short ndays;
+ ndays = read_16(inp); pktlen -= 2;
+ if( ndays )
+ expiredate = timestamp + ndays * 86400L;
+ else
+ expiredate = 0;
+
+ max_expiredate=expiredate;
+ }
+ algorithm = iobuf_get_noeof(inp); pktlen--;
+ if( list_mode )
+ printf(":%s key packet:\n"
+ "\tversion %d, algo %d, created %lu, expires %lu\n",
+ pkttype == PKT_PUBLIC_KEY? "public" :
+ pkttype == PKT_SECRET_KEY? "secret" :
+ pkttype == PKT_PUBLIC_SUBKEY? "public sub" :
+ pkttype == PKT_SECRET_SUBKEY? "secret sub" : "??",
+ version, algorithm, timestamp, expiredate );
+
+ if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) {
+ PKT_secret_key *sk = pkt->pkt.secret_key;
+
+ sk->timestamp = timestamp;
+ sk->expiredate = expiredate;
+ sk->max_expiredate = max_expiredate;
+ sk->hdrbytes = hdrlen;
+ sk->version = version;
+ sk->is_primary = pkttype == PKT_SECRET_KEY;
+ sk->pubkey_algo = algorithm;
+ sk->req_usage = 0;
+ sk->pubkey_usage = 0; /* not yet used */
+ }
+ else {
+ PKT_public_key *pk = pkt->pkt.public_key;
+
+ pk->timestamp = timestamp;
+ pk->expiredate = expiredate;
+ pk->max_expiredate = max_expiredate;
+ pk->hdrbytes = hdrlen;
+ pk->version = version;
+ pk->is_primary = pkttype == PKT_PUBLIC_KEY;
+ pk->pubkey_algo = algorithm;
+ pk->req_usage = 0;
+ pk->pubkey_usage = 0; /* not yet used */
+ pk->is_revoked = 0;
+ pk->is_disabled = 0;
+ pk->keyid[0] = 0;
+ pk->keyid[1] = 0;
+ }
+ nskey = pubkey_get_nskey( algorithm );
+ npkey = pubkey_get_npkey( algorithm );
+ if( !npkey ) {
+ if( list_mode )
+ printf("\tunknown algorithm %d\n", algorithm );
+ unknown_pubkey_warning( algorithm );
+ }
+
+
+ if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) {
+ PKT_secret_key *sk = pkt->pkt.secret_key;
+ byte temp[16];
+
+ if( !npkey ) {
+ sk->skey[0] = mpi_set_opaque( NULL,
+ read_rest(inp, pktlen), pktlen );
+ pktlen = 0;
+ goto leave;
+ }
+
+ for(i=0; i < npkey; i++ ) {
+ n = pktlen; sk->skey[i] = mpi_read(inp, &n, 0 ); pktlen -=n;
+ if( list_mode ) {
+ printf( "\tskey[%d]: ", i);
+ mpi_print(stdout, sk->skey[i], mpi_print_mode );
+ putchar('\n');
+ }
+ if (!sk->skey[i])
+ rc = G10ERR_INVALID_PACKET;
+ }
+ if (rc) /* one of the MPIs were bad */
+ goto leave;
+ sk->protect.algo = iobuf_get_noeof(inp); pktlen--;
+ sk->protect.sha1chk = 0;
+ if( sk->protect.algo ) {
+ sk->is_protected = 1;
+ sk->protect.s2k.count = 0;
+ if( sk->protect.algo == 254 || sk->protect.algo == 255 ) {
+ if( pktlen < 3 ) {
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ sk->protect.sha1chk = (sk->protect.algo == 254);
+ sk->protect.algo = iobuf_get_noeof(inp); pktlen--;
+ /* Note that a sk->protect.algo > 110 is illegal, but
+ I'm not erroring on it here as otherwise there
+ would be no way to delete such a key. */
+ sk->protect.s2k.mode = iobuf_get_noeof(inp); pktlen--;
+ sk->protect.s2k.hash_algo = iobuf_get_noeof(inp); pktlen--;
+ /* check for the special GNU extension */
+ if( is_v4 && sk->protect.s2k.mode == 101 ) {
+ for(i=0; i < 4 && pktlen; i++, pktlen-- )
+ temp[i] = iobuf_get_noeof(inp);
+ if( i < 4 || memcmp( temp, "GNU", 3 ) ) {
+ if( list_mode )
+ printf( "\tunknown S2K %d\n",
+ sk->protect.s2k.mode );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ /* here we know that it is a gnu extension
+ * What follows is the GNU protection mode:
+ * All values have special meanings
+ * and they are mapped in the mode with a base of 1000.
+ */
+ sk->protect.s2k.mode = 1000 + temp[3];
+ }
+ switch( sk->protect.s2k.mode ) {
+ case 1:
+ case 3:
+ for(i=0; i < 8 && pktlen; i++, pktlen-- )
+ temp[i] = iobuf_get_noeof(inp);
+ memcpy(sk->protect.s2k.salt, temp, 8 );
+ break;
+ }
+ switch( sk->protect.s2k.mode ) {
+ case 0: if( list_mode ) printf( "\tsimple S2K" );
+ break;
+ case 1: if( list_mode ) printf( "\tsalted S2K" );
+ break;
+ case 3: if( list_mode ) printf( "\titer+salt S2K" );
+ break;
+ case 1001: if( list_mode ) printf( "\tgnu-dummy S2K" );
+ break;
+ default:
+ if( list_mode )
+ printf( "\tunknown %sS2K %d\n",
+ sk->protect.s2k.mode < 1000? "":"GNU ",
+ sk->protect.s2k.mode );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+
+ if( list_mode ) {
+ printf(", algo: %d,%s hash: %d",
+ sk->protect.algo,
+ sk->protect.sha1chk?" SHA1 protection,"
+ :" simple checksum,",
+ sk->protect.s2k.hash_algo );
+ if( sk->protect.s2k.mode == 1
+ || sk->protect.s2k.mode == 3 ) {
+ printf(", salt: ");
+ for(i=0; i < 8; i++ )
+ printf("%02x", sk->protect.s2k.salt[i]);
+ }
+ putchar('\n');
+ }
+
+ if( sk->protect.s2k.mode == 3 ) {
+ if( pktlen < 1 ) {
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ sk->protect.s2k.count = iobuf_get(inp);
+ pktlen--;
+ if( list_mode )
+ printf("\tprotect count: %lu\n",
+ (ulong)sk->protect.s2k.count);
+ }
+ }
+ /* Note that a sk->protect.algo > 110 is illegal, but I'm
+ not erroring on it here as otherwise there would be no
+ way to delete such a key. */
+ else { /* old version; no S2K, so we set mode to 0, hash MD5 */
+ sk->protect.s2k.mode = 0;
+ sk->protect.s2k.hash_algo = DIGEST_ALGO_MD5;
+ if( list_mode )
+ printf( "\tprotect algo: %d (hash algo: %d)\n",
+ sk->protect.algo, sk->protect.s2k.hash_algo );
+ }
+ /* It is really ugly that we don't know the size
+ * of the IV here in cases we are not aware of the algorithm.
+ * so a
+ * sk->protect.ivlen = cipher_get_blocksize(sk->protect.algo);
+ * won't work. The only solution I see is to hardwire it here.
+ * NOTE: if you change the ivlen above 16, don't forget to
+ * enlarge temp.
+ */
+ switch( sk->protect.algo ) {
+ case 7: case 8: case 9: /* reserved for AES */
+ case 10: /* Twofish */
+ sk->protect.ivlen = 16;
+ break;
+ default:
+ sk->protect.ivlen = 8;
+ }
+ if( sk->protect.s2k.mode == 1001 )
+ sk->protect.ivlen = 0;
+
+ if( pktlen < sk->protect.ivlen ) {
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ for(i=0; i < sk->protect.ivlen && pktlen; i++, pktlen-- )
+ temp[i] = iobuf_get_noeof(inp);
+ if( list_mode ) {
+ printf( "\tprotect IV: ");
+ for(i=0; i < sk->protect.ivlen; i++ )
+ printf(" %02x", temp[i] );
+ putchar('\n');
+ }
+ memcpy(sk->protect.iv, temp, sk->protect.ivlen );
+ }
+ else
+ sk->is_protected = 0;
+ /* It does not make sense to read it into secure memory.
+ * If the user is so careless, not to protect his secret key,
+ * we can assume, that he operates an open system :=(.
+ * So we put the key into secure memory when we unprotect it. */
+ if( sk->protect.s2k.mode == 1001 ) {
+ /* better set some dummy stuff here */
+ sk->skey[npkey] = mpi_set_opaque(NULL, m_strdup("dummydata"), 10);
+ pktlen = 0;
+ }
+ else if( is_v4 && sk->is_protected ) {
+ /* ugly; the length is encrypted too, so we read all
+ * stuff up to the end of the packet into the first
+ * skey element */
+ sk->skey[npkey] = mpi_set_opaque(NULL,
+ read_rest(inp, pktlen), pktlen );
+ pktlen = 0;
+ if( list_mode ) {
+ printf("\tencrypted stuff follows\n");
+ }
+ }
+ else { /* v3 method: the mpi length is not encrypted */
+ for(i=npkey; i < nskey; i++ ) {
+ if ( sk->is_protected ) {
+ sk->skey[i] = read_protected_v3_mpi (inp, &pktlen);
+ if( list_mode )
+ printf( "\tskey[%d]: [encrypted]\n", i);
+ }
+ else {
+ n = pktlen;
+ sk->skey[i] = mpi_read(inp, &n, 0 );
+ pktlen -=n;
+ if( list_mode ) {
+ printf( "\tskey[%d]: ", i);
+ mpi_print(stdout, sk->skey[i], mpi_print_mode );
+ putchar('\n');
+ }
+ }
+
+ if (!sk->skey[i])
+ rc = G10ERR_INVALID_PACKET;
+ }
+ if (rc)
+ goto leave;
+
+ sk->csum = read_16(inp); pktlen -= 2;
+ if( list_mode ) {
+ printf("\tchecksum: %04hx\n", sk->csum);
+ }
+ }
+ }
+ else {
+ PKT_public_key *pk = pkt->pkt.public_key;
+
+ if( !npkey ) {
+ pk->pkey[0] = mpi_set_opaque( NULL,
+ read_rest(inp, pktlen), pktlen );
+ pktlen = 0;
+ goto leave;
+ }
+
+ for(i=0; i < npkey; i++ ) {
+ n = pktlen; pk->pkey[i] = mpi_read(inp, &n, 0 ); pktlen -=n;
+ if( list_mode ) {
+ printf( "\tpkey[%d]: ", i);
+ mpi_print(stdout, pk->pkey[i], mpi_print_mode );
+ putchar('\n');
+ }
+ if (!pk->pkey[i])
+ rc = G10ERR_INVALID_PACKET;
+ }
+ if (rc)
+ goto leave;
+ }
+
+ leave:
+ skip_rest(inp, pktlen);
+ return rc;
+}
+
+/* Attribute subpackets have the same format as v4 signature
+ subpackets. This is not part of OpenPGP, but is done in several
+ versions of PGP nevertheless. */
+int
+parse_attribute_subpkts(PKT_user_id *uid)
+{
+ size_t n;
+ int count=0;
+ struct user_attribute *attribs=NULL;
+ const byte *buffer=uid->attrib_data;
+ int buflen=uid->attrib_len;
+ byte type;
+
+ m_free(uid->attribs);
+
+ while(buflen)
+ {
+ n = *buffer++; buflen--;
+ if( n == 255 ) { /* 4 byte length header */
+ if( buflen < 4 )
+ goto too_short;
+ n = (buffer[0] << 24) | (buffer[1] << 16)
+ | (buffer[2] << 8) | buffer[3];
+ buffer += 4;
+ buflen -= 4;
+ }
+ else if( n >= 192 ) { /* 2 byte special encoded length header */
+ if( buflen < 2 )
+ goto too_short;
+ n = (( n - 192 ) << 8) + *buffer + 192;
+ buffer++;
+ buflen--;
+ }
+ if( buflen < n )
+ goto too_short;
+
+ attribs=m_realloc(attribs,(count+1)*sizeof(struct user_attribute));
+ memset(&attribs[count],0,sizeof(struct user_attribute));
+
+ type=*buffer;
+ buffer++;
+ buflen--;
+ n--;
+
+ attribs[count].type=type;
+ attribs[count].data=buffer;
+ attribs[count].len=n;
+ buffer+=n;
+ buflen-=n;
+ count++;
+ }
+
+ uid->attribs=attribs;
+ uid->numattribs=count;
+ return count;
+
+ too_short:
+ log_error("buffer shorter than attribute subpacket\n");
+ uid->attribs=attribs;
+ uid->numattribs=count;
+ return count;
+}
+
+static void setup_user_id(PACKET *packet)
+{
+ packet->pkt.user_id->ref = 1;
+ packet->pkt.user_id->attribs = NULL;
+ packet->pkt.user_id->attrib_data = NULL;
+ packet->pkt.user_id->attrib_len = 0;
+ packet->pkt.user_id->is_primary = 0;
+ packet->pkt.user_id->is_revoked = 0;
+ packet->pkt.user_id->is_expired = 0;
+ packet->pkt.user_id->expiredate = 0;
+ packet->pkt.user_id->created = 0;
+ packet->pkt.user_id->help_key_usage = 0;
+ packet->pkt.user_id->help_key_expire = 0;
+ packet->pkt.user_id->prefs = NULL;
+ packet->pkt.user_id->namehash = NULL;
+}
+
+static int
+parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+ byte *p;
+
+ packet->pkt.user_id = m_alloc(sizeof *packet->pkt.user_id + pktlen);
+ packet->pkt.user_id->len = pktlen;
+
+ setup_user_id(packet);
+
+ p = packet->pkt.user_id->name;
+ for( ; pktlen; pktlen--, p++ )
+ *p = iobuf_get_noeof(inp);
+ *p = 0;
+
+ if( list_mode ) {
+ int n = packet->pkt.user_id->len;
+ printf(":user ID packet: \"");
+ /* fixme: Hey why don't we replace this with print_string?? */
+ for(p=packet->pkt.user_id->name; n; p++, n-- ) {
+ if( *p >= ' ' && *p <= 'z' )
+ putchar(*p);
+ else
+ printf("\\x%02x", *p );
+ }
+ printf("\"\n");
+ }
+ return 0;
+}
+
+
+void
+make_attribute_uidname(PKT_user_id *uid, size_t max_namelen)
+{
+ assert ( max_namelen > 70 );
+ if(uid->numattribs<=0)
+ sprintf(uid->name,"[bad attribute packet of size %lu]",uid->attrib_len);
+ else if(uid->numattribs>1)
+ sprintf(uid->name,"[%d attributes of size %lu]",
+ uid->numattribs,uid->attrib_len);
+ else
+ {
+ /* Only one attribute, so list it as the "user id" */
+
+ if(uid->attribs->type==ATTRIB_IMAGE)
+ {
+ u32 len;
+ byte type;
+
+ if(parse_image_header(uid->attribs,&type,&len))
+ sprintf(uid->name,"[%.20s image of size %lu]",
+ image_type_to_string(type,1),(ulong)len);
+ else
+ sprintf(uid->name,"[invalid image]");
+ }
+ else
+ sprintf(uid->name,"[unknown attribute of size %lu]",
+ (ulong)uid->attribs->len);
+ }
+
+ uid->len = strlen(uid->name);
+}
+
+static int
+parse_attribute( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+ byte *p;
+
+#define EXTRA_UID_NAME_SPACE 71
+ packet->pkt.user_id = m_alloc(sizeof *packet->pkt.user_id
+ + EXTRA_UID_NAME_SPACE);
+
+ setup_user_id(packet);
+
+ packet->pkt.user_id->attrib_data = m_alloc(pktlen);
+ packet->pkt.user_id->attrib_len = pktlen;
+ p = packet->pkt.user_id->attrib_data;
+ for( ; pktlen; pktlen--, p++ )
+ *p = iobuf_get_noeof(inp);
+
+ /* Now parse out the individual attribute subpackets. This is
+ somewhat pointless since there is only one currently defined
+ attribute type (jpeg), but it is correct by the spec. */
+ parse_attribute_subpkts(packet->pkt.user_id);
+
+ make_attribute_uidname(packet->pkt.user_id, EXTRA_UID_NAME_SPACE);
+
+ if( list_mode ) {
+ printf(":attribute packet: %s\n", packet->pkt.user_id->name );
+ }
+ return 0;
+}
+
+
+static int
+parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+ byte *p;
+
+ packet->pkt.comment = m_alloc(sizeof *packet->pkt.comment + pktlen - 1);
+ packet->pkt.comment->len = pktlen;
+ p = packet->pkt.comment->data;
+ for( ; pktlen; pktlen--, p++ )
+ *p = iobuf_get_noeof(inp);
+
+ if( list_mode ) {
+ int n = packet->pkt.comment->len;
+ printf(":%scomment packet: \"", pkttype == PKT_OLD_COMMENT?
+ "OpenPGP draft " : "" );
+ for(p=packet->pkt.comment->data; n; p++, n-- ) {
+ if( *p >= ' ' && *p <= 'z' )
+ putchar(*p);
+ else
+ printf("\\x%02x", *p );
+ }
+ printf("\"\n");
+ }
+ return 0;
+}
+
+
+static void
+parse_trust( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt )
+{
+ int c;
+
+ if (pktlen)
+ {
+ c = iobuf_get_noeof(inp);
+ pktlen--;
+ pkt->pkt.ring_trust = m_alloc( 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 )
+ printf(":trust packet: flag=%02x sigcache=%02x\n",
+ pkt->pkt.ring_trust->trustval,
+ pkt->pkt.ring_trust->sigcache);
+ }
+ else
+ {
+ if( list_mode )
+ printf(":trust packet: empty\n");
+ }
+ skip_rest (inp, pktlen);
+}
+
+
+static int
+parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *pkt, int new_ctb )
+{
+ int rc = 0;
+ int mode, namelen, partial=0;
+ PKT_plaintext *pt;
+ byte *p;
+ int c, i;
+
+ if( pktlen && pktlen < 6 ) {
+ log_error("packet(%d) too short (%lu)\n", pkttype, (ulong)pktlen);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ /* A packet length of zero indicates partial body length. A zero
+ data length isn't a zero length packet due to the header (mode,
+ name, etc), so this is accurate. */
+ if(pktlen==0)
+ partial=1;
+ mode = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
+ namelen = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
+ pt = pkt->pkt.plaintext = m_alloc(sizeof *pkt->pkt.plaintext + namelen -1);
+ pt->new_ctb = new_ctb;
+ pt->mode = mode;
+ pt->namelen = namelen;
+ pt->is_partial = partial;
+ if( pktlen ) {
+ for( i=0; pktlen > 4 && i < namelen; pktlen--, i++ )
+ pt->name[i] = iobuf_get_noeof(inp);
+ }
+ else {
+ for( i=0; i < namelen; i++ )
+ if( (c=iobuf_get(inp)) == -1 )
+ break;
+ else
+ pt->name[i] = c;
+ }
+ pt->timestamp = read_32(inp); if( pktlen) pktlen -= 4;
+ pt->len = pktlen;
+ pt->buf = inp;
+ pktlen = 0;
+
+ if( list_mode ) {
+ printf(":literal data packet:\n"
+ "\tmode %c, created %lu, name=\"",
+ mode >= ' ' && mode <'z'? mode : '?',
+ (ulong)pt->timestamp );
+ for(p=pt->name,i=0; i < namelen; p++, i++ ) {
+ if( *p >= ' ' && *p <= 'z' )
+ putchar(*p);
+ else
+ printf("\\x%02x", *p );
+ }
+ printf("\",\n\traw data: %lu bytes\n", (ulong)pt->len );
+ }
+
+ leave:
+ return rc;
+}
+
+
+static int
+parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *pkt, int new_ctb )
+{
+ PKT_compressed *zd;
+
+ /* pktlen is here 0, but data follows
+ * (this should be the last object in a file or
+ * the compress algorithm should know the length)
+ */
+ zd = pkt->pkt.compressed = m_alloc(sizeof *pkt->pkt.compressed );
+ zd->algorithm = iobuf_get_noeof(inp);
+ zd->len = 0; /* not used */
+ zd->new_ctb = new_ctb;
+ zd->buf = inp;
+ if( list_mode )
+ printf(":compressed packet: algo=%d\n", zd->algorithm);
+ return 0;
+}
+
+
+static int
+parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *pkt, int new_ctb )
+{
+ int rc = 0;
+ PKT_encrypted *ed;
+ unsigned long orig_pktlen = pktlen;
+
+ ed = pkt->pkt.encrypted = m_alloc(sizeof *pkt->pkt.encrypted );
+ ed->len = pktlen;
+ /* we don't know the extralen which is (cipher_blocksize+2)
+ because the algorithm ist not specified in this packet.
+ However, it is only important to know this for some sanity
+ checks on the packet length - it doesn't matter that we can't
+ do it */
+ ed->extralen = 0;
+ ed->buf = NULL;
+ ed->new_ctb = new_ctb;
+ ed->mdc_method = 0;
+ if( pkttype == PKT_ENCRYPTED_MDC ) {
+ /* fixme: add some pktlen sanity checks */
+ int version;
+
+ version = iobuf_get_noeof(inp);
+ if (orig_pktlen)
+ pktlen--;
+ if( version != 1 ) {
+ log_error("encrypted_mdc packet with unknown version %d\n",
+ version);
+ /*skip_rest(inp, pktlen); should we really do this? */
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ ed->mdc_method = DIGEST_ALGO_SHA1;
+ }
+ if( orig_pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ skip_rest(inp, pktlen);
+ goto leave;
+ }
+ if( list_mode ) {
+ if( orig_pktlen )
+ printf(":encrypted data packet:\n\tlength: %lu\n", orig_pktlen);
+ else
+ printf(":encrypted data packet:\n\tlength: unknown\n");
+ if( ed->mdc_method )
+ printf("\tmdc_method: %d\n", ed->mdc_method );
+ }
+
+ ed->buf = inp;
+ pktlen = 0;
+
+ leave:
+ return rc;
+}
+
+
+static int
+parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *pkt, int new_ctb )
+{
+ int rc = 0;
+ PKT_mdc *mdc;
+ byte *p;
+
+ mdc = pkt->pkt.mdc= m_alloc(sizeof *pkt->pkt.mdc );
+ if( list_mode )
+ printf(":mdc packet: length=%lu\n", pktlen);
+ if( !new_ctb || pktlen != 20 ) {
+ log_error("mdc_packet with invalid encoding\n");
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ p = mdc->hash;
+ for( ; pktlen; pktlen--, p++ )
+ *p = iobuf_get_noeof(inp);
+
+ leave:
+ return rc;
+}
+
+
+/*
+ * This packet is internally generated by PGG (by 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 first check that tehre is a unique tag in it.
+ * The format of such a control packet is:
+ * n byte session marker
+ * 1 byte control type CTRLPKT_xxxxx
+ * m byte control data
+ */
+
+static int
+parse_gpg_control( IOBUF inp,
+ int pkttype, unsigned long pktlen, PACKET *packet )
+{
+ byte *p;
+ const byte *sesmark;
+ size_t sesmarklen;
+ int i;
+
+ if ( list_mode )
+ printf(":packet 63: length %lu ", pktlen);
+
+ sesmark = get_session_marker ( &sesmarklen );
+ if ( pktlen < sesmarklen+1 ) /* 1 is for the control bytes */
+ goto skipit;
+ for( i=0; i < sesmarklen; i++, pktlen-- ) {
+ if ( sesmark[i] != iobuf_get_noeof(inp) )
+ goto skipit;
+ }
+ if ( list_mode )
+ puts ("- gpg control packet");
+
+ packet->pkt.gpg_control = m_alloc(sizeof *packet->pkt.gpg_control
+ + pktlen - 1);
+ packet->pkt.gpg_control->control = iobuf_get_noeof(inp); pktlen--;
+ packet->pkt.gpg_control->datalen = pktlen;
+ p = packet->pkt.gpg_control->data;
+ for( ; pktlen; pktlen--, p++ )
+ *p = iobuf_get_noeof(inp);
+
+ return 0;
+
+ skipit:
+ if ( list_mode ) {
+ int c;
+
+ i=0;
+ printf("- private (rest length %lu)\n", pktlen);
+ if( iobuf_in_block_mode(inp) ) {
+ while( (c=iobuf_get(inp)) != -1 )
+ dump_hex_line(c, &i);
+ }
+ else {
+ for( ; pktlen; pktlen-- )
+ dump_hex_line(iobuf_get(inp), &i);
+ }
+ putchar('\n');
+ }
+ skip_rest(inp,pktlen);
+ return G10ERR_INVALID_PACKET;
+}
+
+/* create a gpg control packet to be used internally as a placeholder */
+PACKET *
+create_gpg_control( ctrlpkttype_t type, const byte *data, size_t datalen )
+{
+ PACKET *packet;
+ byte *p;
+
+ packet = m_alloc( sizeof *packet );
+ init_packet(packet);
+ packet->pkttype = PKT_GPG_CONTROL;
+ packet->pkt.gpg_control = m_alloc(sizeof *packet->pkt.gpg_control
+ + datalen - 1);
+ packet->pkt.gpg_control->control = type;
+ packet->pkt.gpg_control->datalen = datalen;
+ p = packet->pkt.gpg_control->data;
+ for( ; datalen; datalen--, p++ )
+ *p = *data++;
+
+ return packet;
+}