gnupg/g10/parse-packet.c

1793 lines
46 KiB
C

/* parse-packet.c - read packets
* Copyright (C) 1998, 1999, 2000 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 <gcrypt.h>
#include "packet.h"
#include "iobuf.h"
#include "util.h"
#include "filter.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 reqtype,
ulong *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_photo_id( 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 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 = (opt.debug & DBG_MPI_VALUE);
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 packets of the given type.
*/
#ifdef DEBUG_PARSE_PACKET
int
dbg_search_packet( IOBUF inp, PACKET *pkt, int pkttype, ulong *retpos,
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 );
} while( skip );
return rc;
}
#else
int
search_packet( IOBUF inp, PACKET *pkt, int pkttype, ulong *retpos )
{
int skip, rc;
do {
rc = parse( inp, pkt, pkttype, 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, ulong 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, ulong 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 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
* 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,
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;
*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) near %lu\n",
iobuf_where(inp), ctb, iobuf_tell(inp) );
rc = GPGERR_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 = GPGERR_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 = GPGERR_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 = GPGERR_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( out && pkttype ) {
if( iobuf_write( out, hdr, hdrlen ) == -1 )
rc = GPGERR_WRITE_FILE;
else
rc = copy_packet(inp, out, pkttype, pktlen );
goto leave;
}
if( do_skip || !pkttype || (reqtype && pkttype != reqtype) ) {
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 = GPGERR_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 );
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 );
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 = gcry_xcalloc( 1,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 );
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_PHOTO_ID:
pkt->pkttype = pkttype = PKT_USER_ID; /* must fix it */
rc = parse_photo_id(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;
default:
skip_packet(inp, pkttype, pktlen);
break;
}
leave:
if( !rc && iobuf_error(inp) )
rc = GPGERR_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 GPGERR_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 GPGERR_WRITE_FILE; /* write error */
}
else {
for( ; pktlen; pktlen -= n ) {
n = pktlen > 100 ? 100 : pktlen;
n = iobuf_read( inp, buf, n );
if( n == -1 )
return GPGERR_READ_FILE;
if( iobuf_write(out, buf, n ) )
return GPGERR_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 = gcry_xmalloc( 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 i, version, s2kmode, cipher_algo, hash_algo, seskeylen, minlen;
if( pktlen < 4 ) {
log_error("packet(%d) too short\n", pkttype);
goto leave;
}
version = iobuf_get_noeof(inp); pktlen--;
if( version != 4 ) {
log_error("packet(%d) with unknown version %d\n", pkttype, version);
goto leave;
}
if( pktlen > 200 ) { /* (we encode the seskeylen in a byte) */
log_error("packet(%d) too large\n", pkttype);
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 );
goto leave;
}
seskeylen = pktlen - minlen;
k = packet->pkt.symkey_enc = gcry_xcalloc( 1, 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 0;
}
static int
parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
{
unsigned n;
int i, ndata;
PKT_pubkey_enc *k;
k = packet->pkt.pubkey_enc = gcry_xcalloc( 1,sizeof *packet->pkt.pubkey_enc);
if( pktlen < 12 ) {
log_error("packet(%d) too short\n", pkttype);
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);
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');
}
}
}
leave:
skip_rest(inp, pktlen);
return 0;
}
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 );
}
printf("\t%s%ssubpkt %d len %u (", /*)*/
critical ? "critical ":"",
hashed ? "hashed ":"", type, (unsigned)length );
buffer++;
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:
p = "trust signature";
break;
case SIGSUBPKT_REGEXP:
p = "regular expression";
break;
case SIGSUBPKT_REVOCABLE:
p = "revocable";
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 if( !(*buffer & 0x80) )
p = "[not human readable]";
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 );
print_string( stdout, s+n1, n2, ')' );
}
}
}
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:
p = "key server preferences";
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_PRIV_ADD_SIG:
p = "signs additional user ID";
break;
default: p = "?"; break;
}
printf("%s)\n", p? p: "");
}
/****************
* Returns: >= 0 offset into buffer
* -1 unknown type
* -2 unsupported type
* -3 subpacket too short
*/
static 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_KEY_FLAGS:
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_REVOC_REASON:
if( !n )
break;
return 0;
case SIGSUBPKT_PREF_SYM:
case SIGSUBPKT_PREF_HASH:
case SIGSUBPKT_PREF_COMPR:
case SIGSUBPKT_POLICY:
return 0;
case SIGSUBPKT_PRIMARY_UID:
if ( n != 1 )
break;
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;
}
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_ISSUER:/* issuer key ID */
case SIGSUBPKT_PREF_SYM:
case SIGSUBPKT_PREF_HASH:
case SIGSUBPKT_PREF_COMPR:
case SIGSUBPKT_KEY_FLAGS:
return 1;
case SIGSUBPKT_POLICY: /* Is it enough to show the policy? */
default:
return 0;
}
}
const byte *
enum_sig_subpkt( const byte *buffer, sigsubpkttype_t reqtype,
size_t *ret_n, int *start )
{
int buflen;
int type;
int critical;
int offset;
size_t n;
int seq = 0;
int reqseq = start? *start: 0;
if( !buffer || reqseq == -1 ) {
/* return some value different from NULL to indicate that
* there is no crtitical 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*)&buffer : NULL;
}
buflen = (*buffer << 8) | buffer[1];
buffer += 2;
while( buflen ) {
n = *buffer++; buflen--;
if( n == 255 ) {
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 ) {
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 ) ) {
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 byte *buffer, sigsubpkttype_t reqtype, size_t *ret_n )
{
return enum_sig_subpkt( buffer, reqtype, ret_n, 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_data, reqtype, ret_n );
if( !p )
p = parse_sig_subpkt( sig->unhashed_data, reqtype, ret_n );
return p;
}
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);
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--;
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 = GPGERR_INVALID_PACKET;
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");
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 = GPGERR_INVALID_PACKET;
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");
rc = -1;
goto leave;
}
pktlen -= n;
}
}
if( pktlen < 5 ) { /* sanity check */
log_error("packet(%d) too short\n", pkttype);
rc = GPGERR_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;
/* 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;
}
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 {
sig->keyid[0] = buffer_to_u32(p);
sig->keyid[1] = buffer_to_u32(p+4);
}
}
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_data, SIGSUBPKT_LIST_HASHED, NULL );
parse_sig_subpkt( sig->unhashed_data,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');
}
}
}
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;
if( pktlen < 13 ) {
log_error("packet(%d) too short\n", pkttype);
goto leave;
}
version = iobuf_get_noeof(inp); pktlen--;
if( version != 3 ) {
log_error("onepass_sig with unknown version %d\n", version);
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 0;
}
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;
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 gpg 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);
goto leave;
}
if( pktlen < 11 ) {
log_error("packet(%d) too short\n", pkttype);
goto leave;
}
timestamp = read_32(inp); pktlen -= 4;
if( is_v4 )
expiredate = 0; /* have to get it from the selfsignature */
else {
unsigned short ndays;
ndays = read_16(inp); pktlen -= 2;
if( ndays )
expiredate = timestamp + ndays * 86400L;
else
expiredate = 0;
}
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->hdrbytes = hdrlen;
sk->version = version;
sk->is_primary = pkttype == PKT_SECRET_KEY;
sk->pubkey_algo = algorithm;
sk->req_usage = 0;
sk->pubkey_usage = 0; /* will be set by getkey functions */
}
else {
PKT_public_key *pk = pkt->pkt.public_key;
pk->timestamp = timestamp;
pk->expiredate = expiredate;
pk->hdrbytes = hdrlen;
pk->version = version;
pk->pubkey_algo = algorithm;
pk->req_usage = 0;
pk->pubkey_usage = 0; /* will be set bey getkey functions */
pk->is_revoked = 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] = gcry_mpi_set_opaque( NULL,
read_rest(inp, pktlen), pktlen*8 );
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');
}
}
sk->protect.algo = iobuf_get_noeof(inp); pktlen--;
if( sk->protect.algo ) {
sk->is_protected = 1;
sk->protect.s2k.count = 0;
if( sk->protect.algo == 255 ) {
if( pktlen < 3 ) {
rc = GPGERR_INVALID_PACKET;
goto leave;
}
sk->protect.algo = iobuf_get_noeof(inp); pktlen--;
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 = GPGERR_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 = GPGERR_INVALID_PACKET;
goto leave;
}
if( list_mode ) {
printf(", algo: %d, hash: %d",
sk->protect.algo,
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 = GPGERR_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);
}
}
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;
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 = GPGERR_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, gcry_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 */
sk->skey[npkey] = gcry_mpi_set_opaque(NULL,
read_rest(inp, pktlen), pktlen*8 );
pktlen = 0;
if( list_mode ) {
printf("\tencrypted stuff follows\n");
}
}
else { /* unencrypted v4 or v3 method (where length is not encrypted) */
for(i=npkey; i < nskey; i++ ) {
n = pktlen;
sk->skey[i] = sk->is_protected ? mpi_read_opaque(inp, &n )
: mpi_read( inp, &n, 1 );
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');
}
}
}
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] = gcry_mpi_set_opaque( NULL,
read_rest(inp, pktlen), pktlen*8 );
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');
}
}
}
leave:
skip_rest(inp, pktlen);
return rc;
}
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->len = pktlen;
packet->pkt.user_id->photo = NULL;
packet->pkt.user_id->photolen = 0;
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 wioth 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;
}
/****************
* PGP generates a packet of type 17. We assume this is a photo ID and
* simply store it here as a comment packet.
*/
static int
parse_photo_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
{
byte *p;
packet->pkt.user_id = gcry_xmalloc(sizeof *packet->pkt.user_id + 30);
sprintf( packet->pkt.user_id->name, "[image of size %lu]", pktlen );
packet->pkt.user_id->len = strlen(packet->pkt.user_id->name);
packet->pkt.user_id->photo = gcry_xmalloc(sizeof *packet->pkt.user_id + pktlen);
packet->pkt.user_id->photolen = pktlen;
p = packet->pkt.user_id->photo;
for( ; pktlen; pktlen--, p++ )
*p = iobuf_get_noeof(inp);
if( list_mode ) {
printf(":photo_id 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->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;
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 );
}
static int
parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *pkt, int new_ctb )
{
int mode, namelen;
PKT_plaintext *pt;
byte *p;
int c, i;
if( pktlen && pktlen < 6 ) {
log_error("packet(%d) too short (%lu)\n", pkttype, (ulong)pktlen);
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->new_ctb = new_ctb;
pt->mode = mode;
pt->namelen = namelen;
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 0;
}
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 = gcry_xmalloc(sizeof *pkt->pkt.compressed );
zd->len = 0; /* not yet used */
zd->algorithm = iobuf_get_noeof(inp);
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 )
{
PKT_encrypted *ed;
ed = pkt->pkt.encrypted = gcry_xmalloc(sizeof *pkt->pkt.encrypted );
ed->len = pktlen;
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); pktlen--;
if( version != 1 ) {
log_error("encrypted_mdc packet with unknown version %d\n",
version);
goto leave;
}
ed->mdc_method = GCRY_MD_SHA1;
}
if( pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */
log_error("packet(%d) too short\n", pkttype);
skip_rest(inp, pktlen);
goto leave;
}
if( list_mode ) {
if( pktlen )
printf(":encrypted data packet:\n\tlength: %lu\n", 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 0;
}
static int
parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen,
PACKET *pkt, int new_ctb )
{
PKT_mdc *mdc;
byte *p;
mdc = pkt->pkt.mdc= gcry_xmalloc(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");
goto leave;
}
p = mdc->hash;
for( ; pktlen; pktlen--, p++ )
*p = iobuf_get_noeof(inp);
leave:
return 0;
}