X-Git-Url: http://git.gnupg.org/cgi-bin/gitweb.cgi?p=gnupg.git;a=blobdiff_plain;f=g10%2Fparse-packet.c;h=b67ad7f82db5b855689253fcfdaeae945ef60f27;hp=dffb874b0143ff8a222bc64456e48a47d11114e5;hb=09e3b78ea26246ad7d180030b41b50bb09d543a0;hpb=0070faa0ffd5c366bc20f477a4838b701c20b063 diff --git a/g10/parse-packet.c b/g10/parse-packet.c index dffb874b0..b67ad7f82 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -1,5 +1,6 @@ /* parse-packet.c - read packets - * Copyright (C) 1998, 1999 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -15,7 +16,8 @@ * * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, + * USA. */ #include @@ -26,50 +28,57 @@ #include "packet.h" #include "iobuf.h" +#include "mpi.h" #include "util.h" -#include "dummy-cipher.h" -#include +#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 mpi_print_mode; +static int list_mode; +static FILE *listfp; -static int parse( IOBUF inp, PACKET *pkt, int reqtype, - ulong *retpos, int *skip, IOBUF out, int do_skip - #ifdef DEBUG_PARSE_PACKET +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 +#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 ); + unsigned long pktlen, int partial ); +static void skip_packet( IOBUF inp, int pkttype, + unsigned long pktlen, int partial ); +static void *read_rest( IOBUF inp, size_t pktlen, int partial ); 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); + 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 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 ); static unsigned short read_16(IOBUF inp) @@ -97,7 +106,24 @@ set_packet_list_mode( int mode ) { int old = list_mode; list_mode = mode; - mpi_print_mode = (opt.debug & DBG_MPI_VALUE); + mpi_print_mode = DBG_MPI; + /* We use stdout print only if invoked by the --list-packets + command but switch to stderr in all otehr 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. + + Using stderr is not actually very clean because it bypasses the + logging code but it is a special thing anyay. I am not sure + whether using log_stream() would be better. Perhaps we should + enable the list mdoe only with a special option. */ + if (!listfp) + listfp = opt.list_packets == 2 ? stdout : stderr; return old; } @@ -147,28 +173,28 @@ parse_packet( IOBUF inp, PACKET *pkt ) #endif /**************** - * Like parse packet, but only return packets of the given type. + * Like parse packet, but only return secret or public (sub)key packets. */ #ifdef DEBUG_PARSE_PACKET int -dbg_search_packet( IOBUF inp, PACKET *pkt, int pkttype, ulong *retpos, +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, pkttype, retpos, &skip, NULL, 0, "search", dbg_f, dbg_l ); + 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, int pkttype, ulong *retpos ) +search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid ) { int skip, rc; do { - rc = parse( inp, pkt, pkttype, retpos, &skip, NULL, 0 ); + rc = parse( inp, pkt, with_uid?2:1, retpos, &skip, NULL, 0 ); } while( skip ); return rc; } @@ -208,7 +234,7 @@ copy_all_packets( IOBUF inp, IOBUF out ) */ #ifdef DEBUG_PARSE_PACKET int -dbg_copy_some_packets( IOBUF inp, IOBUF out, ulong stopoff, +dbg_copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff, const char *dbg_f, int dbg_l ) { PACKET pkt; @@ -223,7 +249,7 @@ dbg_copy_some_packets( IOBUF inp, IOBUF out, ulong stopoff, } #else int -copy_some_packets( IOBUF inp, IOBUF out, ulong stopoff ) +copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff ) { PACKET pkt; int skip, rc=0; @@ -270,14 +296,14 @@ skip_some_packets( IOBUF inp, unsigned n ) /**************** - * Parse packet. Set the variable skip points to to 1 if the packet - * should be skipped; this is the case if either there is a - * requested packet type and the parsed packet doesn't match or the + * 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 reqtype, ulong *retpos, +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 @@ -288,7 +314,8 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, unsigned long pktlen; byte hdr[8]; int hdrlen; - int new_ctb = 0; + int new_ctb = 0, partial=0; + int with_uid = (onlykeypkts == 2); *skip = 0; assert( !pkt->pkt.generic ); @@ -302,101 +329,156 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, hdrlen=0; hdr[hdrlen++] = ctb; if( !(ctb & 0x80) ) { - log_error("%s: invalid packet (ctb=%02x) near %lu\n", - iobuf_where(inp), ctb, iobuf_tell(inp) ); + 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; + 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; } - 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 */ + if (pkttype == PKT_COMPRESSED) { + iobuf_set_partial_block_mode(inp, c & 0xff); + pktlen = 0;/* to indicate partial length */ + partial=1; + } + 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. Note that we handled + PKT_COMPRESSED earlier. */ + if(pkttype==PKT_PLAINTEXT || pkttype==PKT_ENCRYPTED + || pkttype==PKT_ENCRYPTED_MDC) + { + iobuf_set_partial_block_mode(inp, c & 0xff); + pktlen = 0;/* to indicate partial length */ + partial=1; + } + else + { + log_error("%s: partial length for invalid" + " packet type %d\n",iobuf_where(inp),pkttype); + rc=G10ERR_INVALID_PACKET; + goto leave; + } + } } } - else { + else + { pkttype = (ctb>>2)&0xf; lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3)); - if( !lenbytes ) { + if( !lenbytes ) + { pktlen = 0; /* don't know the value */ - if( pkttype != PKT_COMPRESSED ) - iobuf_set_block_mode(inp, 1); - } - else { - for( ; lenbytes; lenbytes-- ) { + /* This isn't really partial, but we can treat it the same + in a "read until the end" sort of way. */ + partial=1; + if(pkttype!=PKT_ENCRYPTED && pkttype!=PKT_PLAINTEXT + && pkttype!=PKT_COMPRESSED) + { + log_error ("%s: indeterminate length for invalid" + " packet type %d\n", iobuf_where(inp), pkttype ); + rc = G10ERR_INVALID_PACKET; + goto leave; + } + } + 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 ); + rc = copy_packet(inp, out, pkttype, pktlen, partial ); goto leave; } - if( do_skip || !pkttype || (reqtype && pkttype != reqtype) ) { - skip_rest(inp, pktlen); + 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 ) ) { + iobuf_skip_rest(inp, pktlen, partial); *skip = 1; rc = 0; goto leave; } if( DBG_PACKET ) { - #ifdef DEBUG_PARSE_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 +#else log_debug("parse_packet(iob=%d): type=%d length=%lu%s\n", iobuf_id(inp), pkttype, pktlen, new_ctb?" (new_ctb)":"" ); - #endif +#endif } pkt->pkttype = pkttype; rc = G10ERR_UNKNOWN_PACKET; /* default error */ switch( pkttype ) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: - pkt->pkt.public_key = gcry_xcalloc( 1,sizeof *pkt->pkt.public_key ); + pkt->pkt.public_key = xmalloc_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 = gcry_xcalloc( 1,sizeof *pkt->pkt.secret_key ); + pkt->pkt.secret_key = xmalloc_clear(sizeof *pkt->pkt.secret_key ); rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt ); break; case PKT_SYMKEY_ENC: @@ -406,16 +488,20 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, rc = parse_pubkeyenc(inp, pkttype, pktlen, pkt ); break; case PKT_SIGNATURE: - pkt->pkt.signature = gcry_xcalloc( 1,sizeof *pkt->pkt.signature ); + pkt->pkt.signature = xmalloc_clear(sizeof *pkt->pkt.signature ); rc = parse_signature(inp, pkttype, pktlen, pkt->pkt.signature ); break; case PKT_ONEPASS_SIG: - pkt->pkt.onepass_sig = gcry_xcalloc( 1,sizeof *pkt->pkt.onepass_sig ); + pkt->pkt.onepass_sig = xmalloc_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); @@ -425,17 +511,23 @@ parse( IOBUF inp, PACKET *pkt, int reqtype, ulong *retpos, rc = 0; break; case PKT_PLAINTEXT: - rc = parse_plaintext(inp, pkttype, pktlen, pkt, new_ctb ); + rc = parse_plaintext(inp, pkttype, pktlen, pkt, new_ctb, partial ); 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 ); + rc = parse_encrypted(inp, pkttype, pktlen, pkt, new_ctb, partial ); 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, partial ); + break; default: - skip_packet(inp, pkttype, pktlen); + skip_packet(inp, pkttype, pktlen, partial); break; } @@ -450,25 +542,26 @@ dump_hex_line( int c, int *i ) { if( *i && !(*i%8) ) { if( *i && !(*i%24) ) - printf("\n%4d:", *i ); + fprintf (listfp, "\n%4d:", *i ); else - putchar(' '); + putc (' ', listfp); } if( c == -1 ) - printf(" EOF" ); + fprintf (listfp, " EOF" ); else - printf(" %02x", c ); + fprintf (listfp, " %02x", c ); ++*i; } static int -copy_packet( IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen ) +copy_packet( IOBUF inp, IOBUF out, int pkttype, + unsigned long pktlen, int partial ) { int n; char buf[100]; - if( iobuf_in_block_mode(inp) ) { + if( partial ) { while( (n = iobuf_read( inp, buf, 100 )) != -1 ) if( iobuf_write(out, buf, n ) ) return G10ERR_WRITE_FILE; /* write error */ @@ -495,18 +588,19 @@ copy_packet( IOBUF inp, IOBUF out, int pkttype, unsigned long pktlen ) static void -skip_packet( IOBUF inp, int pkttype, unsigned long pktlen ) +skip_packet( IOBUF inp, int pkttype, unsigned long pktlen, int partial ) { if( list_mode ) { if( pkttype == PKT_MARKER ) - fputs(":marker packet:\n", stdout ); + fputs(":marker packet:\n", listfp ); else - printf(":unknown packet: type %2d, length %lu\n", pkttype, pktlen); + fprintf (listfp, ":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) ) { + fputs("dump:", listfp ); + if( partial ) { while( (c=iobuf_get(inp)) != -1 ) dump_hex_line(c, &i); } @@ -514,40 +608,26 @@ skip_packet( IOBUF inp, int pkttype, unsigned long pktlen ) for( ; pktlen; pktlen-- ) dump_hex_line(iobuf_get(inp), &i); } - putchar('\n'); + putc ('\n', listfp); 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; - } + iobuf_skip_rest(inp,pktlen,partial); } static void * -read_rest( IOBUF inp, size_t pktlen ) +read_rest( IOBUF inp, size_t pktlen, int partial ) { byte *p; int i; - if( iobuf_in_block_mode(inp) ) { + if( partial ) { log_error("read_rest: can't store stream data\n"); p = NULL; } else { - p = gcry_xmalloc( pktlen ); + p = xmalloc( pktlen ); for(i=0; pktlen; pktlen--, i++ ) p[i] = iobuf_get(inp); } @@ -560,19 +640,23 @@ 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--; @@ -594,10 +678,11 @@ parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) } 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 = gcry_xcalloc( 1, sizeof *packet->pkt.symkey_enc + k = packet->pkt.symkey_enc = xmalloc_clear( sizeof *packet->pkt.symkey_enc + seskeylen - 1 ); k->version = version; k->cipher_algo = cipher_algo; @@ -611,43 +696,59 @@ parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) 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); + if(k->seskeylen) + { + for(i=0; i < seskeylen && pktlen; i++, pktlen-- ) + k->seskey[i] = iobuf_get_noeof(inp); + + /* What we're watching out for here is a session key decryptor + with no salt. The RFC says that using salt for this is a + MUST. */ + if(s2kmode!=1 && s2kmode!=3) + log_info(_("WARNING: potentially insecure symmetrically" + " encrypted session key\n")); + } assert( !pktlen ); if( list_mode ) { - printf(":symkey enc packet: version %d, cipher %d, s2k %d, hash %d\n", - version, cipher_algo, s2kmode, hash_algo); + fprintf (listfp, ":symkey enc packet: version %d, cipher %d, s2k %d, hash %d", + version, cipher_algo, s2kmode, hash_algo); + if(seskeylen) + fprintf (listfp, ", seskey %d bits",(seskeylen-1)*8); + fprintf (listfp, "\n"); if( s2kmode == 1 || s2kmode == 3 ) { - printf("\tsalt "); + fprintf (listfp, "\tsalt "); for(i=0; i < 8; i++ ) - printf("%02x", k->s2k.salt[i]); + fprintf (listfp, "%02x", k->s2k.salt[i]); if( s2kmode == 3 ) - printf(", count %lu\n", (ulong)k->s2k.count ); - printf("\n"); + fprintf (listfp, ", count %lu", (ulong)k->s2k.count ); + fprintf (listfp, "\n"); } } leave: - skip_rest(inp, pktlen); - return 0; + iobuf_skip_rest(inp, pktlen, 0); + return rc; } static int parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { - unsigned n; + unsigned int n; + int rc = 0; int i, ndata; PKT_pubkey_enc *k; - k = packet->pkt.pubkey_enc = gcry_xcalloc( 1,sizeof *packet->pkt.pubkey_enc); + k = packet->pkt.pubkey_enc = xmalloc_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; @@ -655,13 +756,13 @@ parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) 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", + fprintf (listfp, ":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 ); + fprintf (listfp, "\tunsupported algorithm %d\n", k->pubkey_algo ); unknown_pubkey_warning( k->pubkey_algo ); k->data[0] = NULL; /* no need to store the encrypted data */ } @@ -670,16 +771,18 @@ parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) 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'); + fprintf (listfp, "\tdata: "); + mpi_print(listfp, k->data[i], mpi_print_mode ); + putc ('\n', listfp); } + if (!k->data[i]) + rc = G10ERR_INVALID_PACKET; } } leave: - skip_rest(inp, pktlen); - return 0; + iobuf_skip_rest(inp, pktlen, 0); + return rc; } @@ -690,74 +793,89 @@ dump_sig_subpkt( int hashed, int type, int critical, const char *p=NULL; int i; - printf("\t%s%ssubpkt %d len %u (", /*)*/ - critical ? "critical ":"", - hashed ? "hashed ":"", type, (unsigned)length ); + /* 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 ) { + fprintf (listfp, + "\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--; + + fprintf (listfp, "\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 ); + fprintf (listfp, "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) ) ); + fprintf (listfp, "sig created %s", strtimestamp( buffer_to_u32(buffer) ) ); break; case SIGSUBPKT_SIG_EXPIRE: if( length >= 4 ) - printf("sig expires after %s", + fprintf (listfp, "sig expires after %s", strtimevalue( buffer_to_u32(buffer) ) ); break; case SIGSUBPKT_EXPORTABLE: if( length ) - printf("%sexportable", *buffer? "":"not "); + fprintf (listfp, "%sexportable", *buffer? "":"not "); break; case SIGSUBPKT_TRUST: - p = "trust signature"; + if(length!=2) + p="[invalid trust subpacket]"; + else + fprintf (listfp, "trust signature of depth %d, value %d",buffer[0],buffer[1]); break; case SIGSUBPKT_REGEXP: - p = "regular expression"; + if(!length) + p="[invalid regexp subpacket]"; + else + fprintf (listfp, "regular expression: \"%s\"",buffer); break; case SIGSUBPKT_REVOCABLE: - p = "revocable"; + if( length ) + fprintf (listfp, "%srevocable", *buffer? "":"not "); break; case SIGSUBPKT_KEY_EXPIRE: if( length >= 4 ) - printf("key expires after %s", + fprintf (listfp, "key expires after %s", strtimevalue( buffer_to_u32(buffer) ) ); break; - case SIGSUBPKT_ARR: - p = "additional recipient request"; - break; case SIGSUBPKT_PREF_SYM: - fputs("pref-sym-algos:", stdout ); + fputs("pref-sym-algos:", listfp ); for( i=0; i < length; i++ ) - printf(" %d", buffer[i] ); + fprintf (listfp, " %d", buffer[i] ); break; case SIGSUBPKT_REV_KEY: - fputs("revocation key: ", stdout ); + fputs("revocation key: ", listfp ); if( length < 22 ) p = "[too short]"; else { - printf("c=%02x a=%d f=", buffer[0], buffer[1] ); + fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1] ); for( i=2; i < length; i++ ) - printf("%02X", buffer[i] ); + fprintf (listfp, "%02X", buffer[i] ); } break; case SIGSUBPKT_ISSUER: if( length >= 8 ) - printf("issuer key ID %08lX%08lX", + fprintf (listfp, "issuer key ID %08lX%08lX", (ulong)buffer_to_u32(buffer), (ulong)buffer_to_u32(buffer+4) ); break; case SIGSUBPKT_NOTATION: { - fputs("notation: ", stdout ); + fputs("notation: ", listfp ); if( length < 8 ) p = "[too short]"; - else if( !(*buffer & 0x80) ) - p = "[not human readable]"; else { const byte *s = buffer; size_t n1, n2; @@ -768,151 +886,240 @@ dump_sig_subpkt( int hashed, int type, int critical, if( 8+n1+n2 != length ) p = "[error]"; else { - print_string( stdout, s, n1, ')' ); - putc( '=', stdout ); - print_string( stdout, s+n1, n2, ')' ); + print_string( listfp, s, n1, ')' ); + putc( '=', listfp ); + + if( *buffer & 0x80 ) + print_string( listfp, s+n1, n2, ')' ); + else + p = "[not human readable]"; } } } break; case SIGSUBPKT_PREF_HASH: - fputs("pref-hash-algos:", stdout ); + fputs("pref-hash-algos:", listfp ); for( i=0; i < length; i++ ) - printf(" %d", buffer[i] ); + fprintf (listfp, " %d", buffer[i] ); break; case SIGSUBPKT_PREF_COMPR: - fputs("pref-zip-algos:", stdout ); + fputs("pref-zip-algos:", listfp ); for( i=0; i < length; i++ ) - printf(" %d", buffer[i] ); + fprintf (listfp, " %d", buffer[i] ); break; case SIGSUBPKT_KS_FLAGS: - p = "key server preferences"; + fputs("key server preferences:",listfp); + for(i=0;i=100 && type<=110) + p="experimental / private subpacket"; + else + p = "?"; break; - default: p = "?"; break; } - printf("%s)\n", p? p: ""); + fprintf (listfp, "%s)\n", p? p: ""); } /**************** - * Returns: >= 0 offset into buffer - * -1 unknown type - * -2 unsupported type - * -3 subpacket too short + * Returns: >= 0 use this offset into buffer + * -1 explicitly reject returning this type + * -2 subpacket too short */ -static int +int parse_one_sig_subpkt( const byte *buffer, size_t n, int type ) { - switch( type ) { - case SIGSUBPKT_SIG_CREATED: - case SIGSUBPKT_SIG_EXPIRE: - case SIGSUBPKT_KEY_EXPIRE: - if( n < 4 ) - break; - return 0; - case SIGSUBPKT_EXPORTABLE: - 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_PREF_SYM: - case SIGSUBPKT_PREF_HASH: - case SIGSUBPKT_PREF_COMPR: - case SIGSUBPKT_POLICY: - return 0; - case SIGSUBPKT_PRIV_ADD_SIG: - /* because we use private data, we check the GNUPG marker */ - if( n < 24 ) - break; - if( buffer[0] != 'G' || buffer[1] != 'P' || buffer[2] != 'G' ) - return -2; - return 3; - default: return -1; - } - return -3; + 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_PREF_KS: + case SIGSUBPKT_FEATURES: + case SIGSUBPKT_REGEXP: + return 0; + case SIGSUBPKT_SIGNATURE: + case SIGSUBPKT_EXPORTABLE: + case SIGSUBPKT_REVOCABLE: + case SIGSUBPKT_REVOC_REASON: + if( !n ) + break; + return 0; + case SIGSUBPKT_ISSUER: /* issuer key ID */ + if( n < 8 ) + 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 + packet. */ + if(n<8 || 8+((buffer[4]<<8)|buffer[5])+((buffer[6]<<8)|buffer[7]) != 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 0; + } + return -2; } - +/* Not many critical notations we understand yet... */ static int -can_handle_critical( const byte *buffer, size_t n, int type ) +can_handle_critical_notation(const byte *name,size_t len) { - switch( type ) { - case SIGSUBPKT_NOTATION: - if( n >= 8 && (*buffer & 0x80) ) - return 1; /* human readable is handled */ - return 0; + 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; - case SIGSUBPKT_SIG_CREATED: - case SIGSUBPKT_SIG_EXPIRE: - case SIGSUBPKT_KEY_EXPIRE: - case SIGSUBPKT_EXPORTABLE: - case SIGSUBPKT_ISSUER:/* issuer key ID */ - case SIGSUBPKT_PREF_SYM: - case SIGSUBPKT_PREF_HASH: - case SIGSUBPKT_PREF_COMPR: - return 1; + return 0; +} - case SIGSUBPKT_POLICY: /* Is enough to show the policy? */ - default: +static int +can_handle_critical( const byte *buffer, size_t n, int type ) +{ + switch( type ) + { + case SIGSUBPKT_NOTATION: + if(n>=8) + return can_handle_critical_notation(buffer+8,(buffer[4]<<8)|buffer[5]); + else return 0; + case SIGSUBPKT_SIGNATURE: + 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_TRUST: + case SIGSUBPKT_REGEXP: + /* Is it enough to show the policy or keyserver? */ + case SIGSUBPKT_POLICY: + case SIGSUBPKT_PREF_KS: + return 1; + + default: + return 0; } } const byte * -enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, - size_t *ret_n, int *start ) +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; + int critical_dummy; int offset; size_t n; int seq = 0; int reqseq = start? *start: 0; - if( !buffer || reqseq == -1 ) - return NULL; - buflen = (*buffer << 8) | buffer[1]; - buffer += 2; + 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 ) { + 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[2] << 8) | buffer[3]; buffer += 4; buflen -= 4; - } - else if( n >= 192 ) { + else if( n >= 192 ) { /* 2 byte special encoded length header */ if( buflen < 2 ) goto too_short; n = (( n - 192 ) << 8) + *buffer + 192; @@ -924,28 +1131,30 @@ enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, type = *buffer; if( type & 0x80 ) { type &= 0x7f; - critical = 1; + *critical = 1; } else - critical = 0; + *critical = 0; if( !(++seq > reqseq) ) ; else if( reqtype == SIGSUBPKT_TEST_CRITICAL ) { - if( critical ) { + if( *critical ) { if( n-1 > buflen+1 ) goto too_short; - if( !can_handle_critical(buffer+1, n-1, type ) ) { - log_info(_("subpacket of type %d has critical bit set\n"), - type); + 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; + *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 ); + type, *critical, buffer, buflen, n ); else if( type == reqtype ) { /* found */ buffer++; n--; @@ -955,13 +1164,11 @@ enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, *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: + log_error("subpacket of type %d too short\n", type); return NULL; case -1: - BUG(); /* not yet needed */ + return NULL; default: break; } @@ -979,7 +1186,8 @@ enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, return NULL; /* end of packets; not found */ too_short: - log_error("buffer shorter than subpacket\n"); + if(opt.verbose) + log_info("buffer shorter than subpacket\n"); if( start ) *start = -1; return NULL; @@ -987,25 +1195,51 @@ enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, const byte * -parse_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype, size_t *ret_n ) +parse_sig_subpkt (const subpktarea_t *buffer, sigsubpkttype_t reqtype, + size_t *ret_n) { - return enum_sig_subpkt( buffer, reqtype, ret_n, NULL ); + 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 ) +parse_sig_subpkt2 (PKT_signature *sig, sigsubpkttype_t reqtype, + size_t *ret_n ) { const byte *p; - p = parse_sig_subpkt( sig->hashed_data, reqtype, ret_n ); + p = parse_sig_subpkt (sig->hashed, reqtype, ret_n ); if( !p ) - p = parse_sig_subpkt( sig->unhashed_data, reqtype, ret_n ); + 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=xrealloc(sig->revkey, + sizeof(struct revocation_key *)*(sig->numrevkeys+1)); + sig->revkey[sig->numrevkeys]=revkey; + sig->numrevkeys++; + } + } +} - -static int +int parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, PKT_signature *sig ) { @@ -1024,6 +1258,7 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, 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; } @@ -1038,6 +1273,8 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, } 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 ) { @@ -1046,11 +1283,12 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, goto leave; } if( n ) { - sig->hashed_data = gcry_xmalloc( n + 2 ); - sig->hashed_data[0] = n >> 8; - sig->hashed_data[1] = n; - if( iobuf_read(inp, sig->hashed_data+2, n ) != n ) { - log_error("premature eof while reading hashed signature data\n"); + sig->hashed = xmalloc (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; } @@ -1063,11 +1301,12 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, goto leave; } if( n ) { - sig->unhashed_data = gcry_xmalloc( n + 2 ); - sig->unhashed_data[0] = n >> 8; - sig->unhashed_data[1] = n; - if( iobuf_read(inp, sig->unhashed_data+2, n ) != n ) { - log_error("premature eof while reading unhashed signature data\n"); + sig->unhashed = xmalloc (sizeof(*sig->unhashed) + n - 1 ); + sig->unhashed->size = n; + 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; } @@ -1084,34 +1323,90 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, 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_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_data, SIGSUBPKT_TEST_CRITICAL, NULL) - || !parse_sig_subpkt( sig->unhashed_data, SIGSUBPKT_TEST_CRITICAL, - NULL) ) - { - sig->flags.unknown_critical = 1; - } + 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) + sig->timestamp = buffer_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_subpkt( sig->hashed_data, 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 { + if(p) + { sig->keyid[0] = buffer_to_u32(p); sig->keyid[1] = buffer_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) + 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_PREF_KS,NULL); + if(p) + sig->flags.pref_ks=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" + fprintf (listfp, ":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, @@ -1120,20 +1415,19 @@ parse_signature( IOBUF inp, int pkttype, unsigned long pktlen, sig->digest_algo, sig->digest_start[0], sig->digest_start[1] ); if( is_v4 ) { - parse_sig_subpkt( sig->hashed_data, SIGSUBPKT_LIST_HASHED, NULL ); - parse_sig_subpkt( sig->unhashed_data,SIGSUBPKT_LIST_UNHASHED, NULL); + 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 ); + fprintf (listfp, "\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] = gcry_mpi_set_opaque(NULL, - read_rest(inp, pktlen), pktlen*8 ); + sig->data[0]= mpi_set_opaque(NULL, read_rest(inp, pktlen, 0), pktlen ); pktlen = 0; } else { @@ -1142,15 +1436,17 @@ parse_signature( IOBUF inp, int pkttype, unsigned long 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'); + fprintf (listfp, "\tdata: "); + mpi_print(listfp, sig->data[i], mpi_print_mode ); + putc ('\n', listfp); } + if (!sig->data[i]) + rc = G10ERR_INVALID_PACKET; } } leave: - skip_rest(inp, pktlen); + iobuf_skip_rest(inp, pktlen, 0); return rc; } @@ -1160,14 +1456,17 @@ 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--; @@ -1177,7 +1476,7 @@ parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen, 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" + fprintf (listfp, ":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, @@ -1185,11 +1484,56 @@ parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen, leave: - skip_rest(inp, pktlen); - return 0; + iobuf_skip_rest(inp, pktlen, 0); + 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 = xmalloc (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"); + xfree (buf); + return NULL; + } + + /* convert buffer into an opaque MPI */ + val = mpi_set_opaque (NULL, buf, p-buf); + return val; +} static int @@ -1198,7 +1542,7 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, { int i, version, algorithm; unsigned n; - unsigned long timestamp, expiredate; + unsigned long timestamp, expiredate, max_expiredate; int npkey, nskey; int is_v4=0; int rc=0; @@ -1208,35 +1552,39 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, /* 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: \"" ); + fprintf (listfp, ":rfc1991 comment packet: \"" ); for( ; pktlen; pktlen-- ) { int c; c = iobuf_get_noeof(inp); if( c >= ' ' && c <= 'z' ) - putchar(c); + putc (c, listfp); else - printf("\\x%02x", c ); + fprintf (listfp, "\\x%02x", c ); } - printf("\"\n"); + fprintf (listfp, "\"\n"); } - skip_rest(inp, pktlen); + iobuf_skip_rest(inp, pktlen, 0); 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 ) + 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; @@ -1244,10 +1592,12 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, expiredate = timestamp + ndays * 86400L; else expiredate = 0; + + max_expiredate=expiredate; } algorithm = iobuf_get_noeof(inp); pktlen--; if( list_mode ) - printf(":%s key packet:\n" + fprintf (listfp, ":%s key packet:\n" "\tversion %d, algo %d, created %lu, expires %lu\n", pkttype == PKT_PUBLIC_KEY? "public" : pkttype == PKT_SECRET_KEY? "secret" : @@ -1260,10 +1610,12 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, 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 { @@ -1271,10 +1623,15 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, 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; } @@ -1282,7 +1639,7 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, npkey = pubkey_get_npkey( algorithm ); if( !npkey ) { if( list_mode ) - printf("\tunknown algorithm %d\n", algorithm ); + fprintf (listfp, "\tunknown algorithm %d\n", algorithm ); unknown_pubkey_warning( algorithm ); } @@ -1290,10 +1647,11 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *sk = pkt->pkt.secret_key; byte temp[16]; + size_t snlen = 0; if( !npkey ) { - sk->skey[0] = gcry_mpi_set_opaque( NULL, - read_rest(inp, pktlen), pktlen*8 ); + sk->skey[0] = mpi_set_opaque( NULL, + read_rest(inp, pktlen, 0), pktlen ); pktlen = 0; goto leave; } @@ -1301,23 +1659,50 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, 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'); + fprintf (listfp, "\tskey[%d]: ", i); + mpi_print(listfp, sk->skey[i], mpi_print_mode ); + putc ('\n', listfp); } + 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 == 255 ) { + 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 ) + fprintf (listfp, "\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: @@ -1327,31 +1712,40 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, break; } switch( sk->protect.s2k.mode ) { - case 0: if( list_mode ) printf( "\tsimple S2K" ); + case 0: if( list_mode ) fprintf (listfp, "\tsimple S2K" ); + break; + case 1: if( list_mode ) fprintf (listfp, "\tsalted S2K" ); break; - case 1: if( list_mode ) printf( "\tsalted S2K" ); + case 3: if( list_mode ) fprintf (listfp, "\titer+salt S2K" ); break; - case 3: if( list_mode ) printf( "\titer+salt S2K" ); + case 1001: if( list_mode ) fprintf (listfp, + "\tgnu-dummy S2K" ); + break; + case 1002: if (list_mode) fprintf (listfp, + "\tgnu-divert-to-card S2K"); break; default: if( list_mode ) - printf( "\tunknown S2K %d\n", - sk->protect.s2k.mode ); + fprintf (listfp, "\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, hash: %d", + fprintf (listfp, ", 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: "); + fprintf (listfp, ", salt: "); for(i=0; i < 8; i++ ) - printf("%02x", sk->protect.s2k.salt[i]); + fprintf (listfp, "%02x", sk->protect.s2k.salt[i]); } - putchar('\n'); + putc ('\n', listfp); } if( sk->protect.s2k.mode == 3 ) { @@ -1362,15 +1756,31 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, sk->protect.s2k.count = iobuf_get(inp); pktlen--; if( list_mode ) - printf("\tprotect count: %lu\n", + fprintf (listfp, "\tprotect count: %lu\n", (ulong)sk->protect.s2k.count); } + else if( sk->protect.s2k.mode == 1002 ) { + /* Read the serial number. */ + if (pktlen < 1) { + rc = G10ERR_INVALID_PACKET; + goto leave; + } + snlen = iobuf_get (inp); + pktlen--; + if (pktlen < snlen || snlen == -1) { + rc = G10ERR_INVALID_PACKET; + goto leave; + } + } } + /* 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 = GCRY_MD_MD5; + sk->protect.s2k.hash_algo = DIGEST_ALGO_MD5; if( list_mode ) - printf( "\tprotect algo: %d (hash algo: %d)\n", + fprintf (listfp, "\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 @@ -1389,6 +1799,11 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, default: sk->protect.ivlen = 8; } + if( sk->protect.s2k.mode == 1001 ) + sk->protect.ivlen = 0; + else if( sk->protect.s2k.mode == 1002 ) + sk->protect.ivlen = snlen < 16? snlen : 16; + if( pktlen < sk->protect.ivlen ) { rc = G10ERR_INVALID_PACKET; goto leave; @@ -1396,10 +1811,12 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, for(i=0; i < sk->protect.ivlen && pktlen; i++, pktlen-- ) temp[i] = iobuf_get_noeof(inp); if( list_mode ) { - printf( "\tprotect IV: "); + fprintf (listfp, + sk->protect.s2k.mode == 1002? "\tserial-number: " + : "\tprotect IV: "); for(i=0; i < sk->protect.ivlen; i++ ) - printf(" %02x", temp[i] ); - putchar('\n'); + fprintf (listfp, " %02x", temp[i] ); + putc ('\n', listfp); } memcpy(sk->protect.iv, temp, sk->protect.ivlen ); } @@ -1409,36 +1826,50 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, * 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( is_v4 && sk->is_protected ) { + if( sk->protect.s2k.mode == 1001 + || sk->protect.s2k.mode == 1002 ) { + /* better set some dummy stuff here */ + sk->skey[npkey] = mpi_set_opaque(NULL, xstrdup("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 (which is the one indexed by npkey) */ - sk->skey[npkey] = gcry_mpi_set_opaque(NULL, - read_rest(inp, pktlen), pktlen*8 ); + * skey element */ + sk->skey[npkey] = mpi_set_opaque(NULL, + read_rest(inp, pktlen, 0),pktlen); pktlen = 0; if( list_mode ) { - printf("\tencrypted stuff follows\n"); + fprintf (listfp, "\tencrypted stuff follows\n"); } } else { /* v3 method: the mpi length is not encrypted */ for(i=npkey; i < nskey; i++ ) { - n = pktlen; - sk->skey[i] = mpi_read_opaque(inp, &n ); - pktlen -=n; - if( list_mode ) { - printf( "\tskey[%d]: ", i); - if( sk->is_protected ) - printf( "[encrypted]\n"); - else { - mpi_print(stdout, sk->skey[i], mpi_print_mode ); - putchar('\n'); - } - } + if ( sk->is_protected ) { + sk->skey[i] = read_protected_v3_mpi (inp, &pktlen); + if( list_mode ) + fprintf (listfp, "\tskey[%d]: [encrypted]\n", i); + } + else { + n = pktlen; + sk->skey[i] = mpi_read(inp, &n, 0 ); + pktlen -=n; + if( list_mode ) { + fprintf (listfp, "\tskey[%d]: ", i); + mpi_print(listfp, sk->skey[i], mpi_print_mode ); + putc ('\n', listfp); + } + } + + 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); + fprintf (listfp, "\tchecksum: %04hx\n", sk->csum); } } } @@ -1446,8 +1877,8 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, PKT_public_key *pk = pkt->pkt.public_key; if( !npkey ) { - pk->pkey[0] = gcry_mpi_set_opaque( NULL, - read_rest(inp, pktlen), pktlen*8 ); + pk->pkey[0] = mpi_set_opaque( NULL, + read_rest(inp, pktlen, 0), pktlen ); pktlen = 0; goto leave; } @@ -1455,26 +1886,96 @@ parse_key( IOBUF inp, int pkttype, unsigned long pktlen, 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'); + fprintf (listfp, "\tpkey[%d]: ", i); + mpi_print(listfp, pk->pkey[i], mpi_print_mode ); + putc ('\n', listfp); } + if (!pk->pkey[i]) + rc = G10ERR_INVALID_PACKET; } + if (rc) + goto leave; } leave: - skip_rest(inp, pktlen); + iobuf_skip_rest(inp, pktlen, 0); 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; + + xfree(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=xrealloc(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: + if(opt.verbose) + log_info("buffer shorter than attribute subpacket\n"); + uid->attribs=attribs; + uid->numattribs=count; + return count; +} + static int parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) { byte *p; - packet->pkt.user_id = gcry_xmalloc(sizeof *packet->pkt.user_id + pktlen); + packet->pkt.user_id = xmalloc_clear(sizeof *packet->pkt.user_id + pktlen); packet->pkt.user_id->len = pktlen; + packet->pkt.user_id->ref=1; + p = packet->pkt.user_id->name; for( ; pktlen; pktlen--, p++ ) *p = iobuf_get_noeof(inp); @@ -1482,27 +1983,88 @@ parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) if( list_mode ) { int n = packet->pkt.user_id->len; - printf(":user ID packet: \""); - /* fixme: Hey why don't we replace this wioth print_string?? */ + fprintf (listfp, ":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); + putc (*p, listfp); else - printf("\\x%02x", *p ); + fprintf (listfp, "\\x%02x", *p ); } - printf("\"\n"); + fprintf (listfp, "\"\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 = xmalloc_clear(sizeof *packet->pkt.user_id + + EXTRA_UID_NAME_SPACE); + packet->pkt.user_id->ref=1; + packet->pkt.user_id->attrib_data = xmalloc(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 ) { + fprintf (listfp, ":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 = gcry_xmalloc(sizeof *packet->pkt.comment + pktlen - 1); + packet->pkt.comment = xmalloc(sizeof *packet->pkt.comment + pktlen - 1); packet->pkt.comment->len = pktlen; p = packet->pkt.comment->data; for( ; pktlen; pktlen--, p++ ) @@ -1510,15 +2072,15 @@ parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) if( list_mode ) { int n = packet->pkt.comment->len; - printf(":%scomment packet: \"", pkttype == PKT_OLD_COMMENT? + fprintf (listfp, ":%scomment packet: \"", pkttype == PKT_OLD_COMMENT? "OpenPGP draft " : "" ); for(p=packet->pkt.comment->data; n; p++, n-- ) { if( *p >= ' ' && *p <= 'z' ) - putchar(*p); + putc (*p, listfp); else - printf("\\x%02x", *p ); + fprintf (listfp, "\\x%02x", *p ); } - printf("\"\n"); + fprintf (listfp, "\"\n"); } return 0; } @@ -1527,35 +2089,59 @@ parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet ) static void parse_trust( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt ) { - int c; - - c = iobuf_get_noeof(inp); - pkt->pkt.ring_trust = gcry_xmalloc( sizeof *pkt->pkt.ring_trust ); - pkt->pkt.ring_trust->trustval = c; - if( list_mode ) - printf(":trust packet: flag=%02x\n", c ); + int c; + + if (pktlen) + { + c = iobuf_get_noeof(inp); + 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 ) + fprintf (listfp, ":trust packet: flag=%02x sigcache=%02x\n", + pkt->pkt.ring_trust->trustval, + pkt->pkt.ring_trust->sigcache); + } + else + { + if( list_mode ) + fprintf (listfp, ":trust packet: empty\n"); + } + iobuf_skip_rest (inp, pktlen, 0); } static int parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, - PACKET *pkt, int new_ctb ) + PACKET *pkt, int new_ctb, int partial ) { + int rc = 0; int mode, namelen; PKT_plaintext *pt; byte *p; int c, i; - if( pktlen && pktlen < 6 ) { + if( !partial && pktlen < 6 ) { log_error("packet(%d) too short (%lu)\n", pkttype, (ulong)pktlen); + rc = G10ERR_INVALID_PACKET; goto leave; } mode = iobuf_get_noeof(inp); if( pktlen ) pktlen--; namelen = iobuf_get_noeof(inp); if( pktlen ) pktlen--; - pt = pkt->pkt.plaintext = gcry_xmalloc(sizeof *pkt->pkt.plaintext + namelen -1); + pt = pkt->pkt.plaintext = xmalloc(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); @@ -1573,21 +2159,25 @@ parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen, pktlen = 0; if( list_mode ) { - printf(":literal data packet:\n" - "\tmode %c, created %lu, name=\"", - mode >= ' ' && mode <'z'? mode : '?', + fprintf (listfp, ":literal data packet:\n" + "\tmode %c (%X), created %lu, name=\"", + mode >= ' ' && mode <'z'? mode : '?', mode, (ulong)pt->timestamp ); for(p=pt->name,i=0; i < namelen; p++, i++ ) { if( *p >= ' ' && *p <= 'z' ) - putchar(*p); + putc (*p, listfp); else - printf("\\x%02x", *p ); + fprintf (listfp, "\\x%02x", *p ); } - printf("\",\n\traw data: %lu bytes\n", (ulong)pt->len ); + fprintf (listfp, "\",\n\traw data: "); + if(partial) + fprintf (listfp, "unknown length\n"); + else + fprintf (listfp, "%lu bytes\n", (ulong)pt->len ); } leave: - return 0; + return rc; } @@ -1601,64 +2191,181 @@ parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen, * (this should be the last object in a file or * the compress algorithm should know the length) */ - zd = pkt->pkt.compressed = gcry_xmalloc(sizeof *pkt->pkt.compressed ); - zd->len = 0; /* not yet used */ + zd = pkt->pkt.compressed = xmalloc(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); + fprintf (listfp, ":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 ) + PACKET *pkt, int new_ctb, int partial ) { + int rc = 0; PKT_encrypted *ed; + unsigned long orig_pktlen = pktlen; - ed = pkt->pkt.encrypted = gcry_xmalloc(sizeof *pkt->pkt.encrypted ); + ed = pkt->pkt.encrypted = xmalloc(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->is_partial = partial; ed->mdc_method = 0; if( pkttype == PKT_ENCRYPTED_MDC ) { - /* test: this is the new encrypted_mdc packet */ /* fixme: add some pktlen sanity checks */ - int version, method; + int version; - version = iobuf_get_noeof(inp); pktlen--; + 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; } - method = iobuf_get_noeof(inp); pktlen--; - if( method != GCRY_MD_SHA1 ) { - log_error("encrypted_mdc does not use SHA1 method\n" ); - goto leave; - } - ed->mdc_method = method; + ed->mdc_method = DIGEST_ALGO_SHA1; } - if( pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */ + if( orig_pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */ log_error("packet(%d) too short\n", pkttype); - skip_rest(inp, pktlen); + rc = G10ERR_INVALID_PACKET; + iobuf_skip_rest(inp, pktlen, partial); goto leave; } if( list_mode ) { - if( pktlen ) - printf(":encrypted data packet:\n\tlength: %lu\n", pktlen); + if( orig_pktlen ) + fprintf (listfp, ":encrypted data packet:\n\tlength: %lu\n", + orig_pktlen); else - printf(":encrypted data packet:\n\tlength: unknown\n"); + fprintf (listfp, ":encrypted data packet:\n\tlength: unknown\n"); if( ed->mdc_method ) - printf("\tmdc_method: %d\n", ed->mdc_method ); + fprintf (listfp, "\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= xmalloc(sizeof *pkt->pkt.mdc ); + if( list_mode ) + fprintf (listfp, ":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, int partial ) +{ + byte *p; + const byte *sesmark; + size_t sesmarklen; + int i; + + if ( list_mode ) + fprintf (listfp, ":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 = xmalloc(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; + fprintf (listfp, "- private (rest length %lu)\n", pktlen); + if( partial ) { + while( (c=iobuf_get(inp)) != -1 ) + dump_hex_line(c, &i); + } + else { + for( ; pktlen; pktlen-- ) + dump_hex_line(iobuf_get(inp), &i); + } + putc ('\n', listfp); + } + iobuf_skip_rest(inp,pktlen, 0); + 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 = xmalloc( sizeof *packet ); + init_packet(packet); + packet->pkttype = PKT_GPG_CONTROL; + packet->pkt.gpg_control = xmalloc(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; +}