diff options
Diffstat (limited to 'g10/mainproc.c')
-rw-r--r-- | g10/mainproc.c | 1301 |
1 files changed, 807 insertions, 494 deletions
diff --git a/g10/mainproc.c b/g10/mainproc.c index 40b9bd20a..22711cf59 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -1,5 +1,6 @@ /* mainproc.c - handle packets - * Copyright (C) 1998,1999,2000,2001,2002,2003 Free Software Foundation, Inc. + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, + * 2005, 2006 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 <config.h> @@ -25,9 +27,9 @@ #include <assert.h> #include <time.h> +#include "gpg.h" #include "packet.h" #include "iobuf.h" -#include "memory.h" #include "options.h" #include "util.h" #include "cipher.h" @@ -49,37 +51,34 @@ struct kidlist_item { }; - /**************** * Structure to hold the context */ typedef struct mainproc_context *CTX; -struct mainproc_context { - struct mainproc_context *anchor; /* may be useful in the future */ - PKT_public_key *last_pubkey; - PKT_secret_key *last_seckey; - PKT_user_id *last_user_id; - md_filter_context_t mfx; - int sigs_only; /* process only signatures and reject all other stuff */ - int encrypt_only; /* process only encrytion messages */ - STRLIST signed_data; - const char *sigfilename; - DEK *dek; - int last_was_session_key; - KBNODE list; /* the current list of packets */ - int have_data; - iobuf_t iobuf; /* used to get the filename etc. */ - int trustletter; /* temp usage in list_node */ - ulong local_id; /* ditto */ - struct kidlist_item *pkenc_list; /* list of encryption packets */ - struct { - int op; - int stop_now; - } pipemode; +struct mainproc_context +{ + struct mainproc_context *anchor; /* May be useful in the future. */ + PKT_public_key *last_pubkey; + PKT_secret_key *last_seckey; + PKT_user_id *last_user_id; + md_filter_context_t mfx; + int sigs_only; /* Process only signatures and reject all other stuff. */ + int encrypt_only; /* Process only encryption messages. */ + STRLIST signed_data; + const char *sigfilename; + DEK *dek; + int last_was_session_key; + KBNODE list; /* The current list of packets. */ + int have_data; + IOBUF iobuf; /* Used to get the filename etc. */ + int trustletter; /* Temporary usage in list_node. */ + ulong symkeys; + struct kidlist_item *pkenc_list; /* List of encryption packets. */ + int any_sig_seen; /* Set to true if a signature packet has been seen. */ }; -static int do_proc_packets( CTX c, iobuf_t a ); +static int do_proc_packets( CTX c, IOBUF a ); static void list_node( CTX c, KBNODE node ); static void proc_tree( CTX c, KBNODE node ); @@ -94,43 +93,28 @@ release_list( CTX c ) release_kbnode( c->list ); while( c->pkenc_list ) { struct kidlist_item *tmp = c->pkenc_list->next; - xfree ( c->pkenc_list ); + xfree( c->pkenc_list ); c->pkenc_list = tmp; } c->pkenc_list = NULL; c->list = NULL; c->have_data = 0; c->last_was_session_key = 0; - c->pipemode.op = 0; - c->pipemode.stop_now = 0; - xfree (c->dek); c->dek = NULL; + xfree(c->dek); c->dek = NULL; } static int add_onepass_sig( CTX c, PACKET *pkt ) { - KBNODE node; + KBNODE node; - if( c->list ) { /* add another packet */ - /* We can only append another onepass packet if the list - * does contain only onepass packets */ - for( node=c->list; node && node->pkt->pkttype == PKT_ONEPASS_SIG; - node = node->next ) - ; - if( node ) { - /* this is not the case, so we flush the current thing and - * allow this packet to start a new verification thing */ - release_list( c ); - c->list = new_kbnode( pkt ); - } - else - add_kbnode( c->list, new_kbnode( pkt )); - } - else /* insert the first one */ - c->list = node = new_kbnode( pkt ); + if ( c->list ) /* add another packet */ + add_kbnode( c->list, new_kbnode( pkt )); + else /* insert the first one */ + c->list = node = new_kbnode( pkt ); - return 1; + return 1; } @@ -142,30 +126,6 @@ add_gpg_control( CTX c, PACKET *pkt ) * Process the last one and reset everything */ release_list(c); } - else if ( pkt->pkt.gpg_control->control == CTRLPKT_PIPEMODE ) { - /* Pipemode control packet */ - if ( pkt->pkt.gpg_control->datalen < 2 ) - log_fatal ("invalid pipemode control packet length\n"); - if (pkt->pkt.gpg_control->data[0] == 1) { - /* start the whole thing */ - assert ( !c->list ); /* we should be in a pretty virgin state */ - assert ( !c->pipemode.op ); - c->pipemode.op = pkt->pkt.gpg_control->data[1]; - } - else if (pkt->pkt.gpg_control->data[0] == 2) { - /* the signed material follows in a plaintext packet */ - assert ( c->pipemode.op == 'B' ); - } - else if (pkt->pkt.gpg_control->data[0] == 3) { - assert ( c->pipemode.op == 'B' ); - release_list (c); - /* and tell the outer loop to terminate */ - c->pipemode.stop_now = 1; - } - else - log_fatal ("invalid pipemode control packet code\n"); - return 0; /* no need to store the packet */ - } if( c->list ) /* add another packet */ add_kbnode( c->list, new_kbnode( pkt )); @@ -216,6 +176,7 @@ add_signature( CTX c, PACKET *pkt ) { KBNODE node; + c->any_sig_seen = 1; if( pkt->pkttype == PKT_SIGNATURE && !c->list ) { /* This is the first signature for the following datafile. * GPG does not write such packets; instead it always uses @@ -238,43 +199,47 @@ add_signature( CTX c, PACKET *pkt ) return 1; } -static void -symkey_decrypt_sesskey (DEK * dek, byte *sesskey, size_t slen) +static int +symkey_decrypt_seskey( DEK *dek, byte *seskey, size_t slen ) { - CIPHER_HANDLE hd; - int n; + gcry_cipher_hd_t hd; - if (slen < 17 || slen > 33) + if(slen < 17 || slen > 33) { log_error ( _("weird size for an encrypted session key (%d)\n"), - (int)slen); - return; + (int)slen); + return G10ERR_BAD_KEY; } - /* we checked the DEK values before, so consider all errors as fatal */ + if (gcry_cipher_open (&hd, dek->algo, GCRY_CIPHER_MODE_CFB, 1)) - BUG(); - if (gcry_cipher_setkey (hd, dek->key, dek->keylen)) - BUG(); - gcry_cipher_setiv (hd, NULL, 0); - gcry_cipher_decrypt (hd, sesskey, slen, NULL, 0); - gcry_cipher_close (hd); - /* check first byte (the cipher algo) */ - if (openpgp_cipher_test_algo (sesskey[0])) - { - log_error (_("invalid symkey algorithm detected (%d)\n"), - sesskey[0]); - return; - } - n = gcry_cipher_get_algo_keylen (sesskey[0]); - if (n > DIM(dek->key)) + BUG (); + if (gcry_cipher_setkey ( hd, dek->key, dek->keylen )) BUG (); - /* now we replace the dek components with the real session key - to decrypt the contents of the sequencing packet. */ - dek->keylen = n; - dek->algo = sesskey[0]; - memcpy (dek->key, sesskey + 1, dek->keylen); - /*log_hexdump ("thekey", dek->key, dek->keylen);*/ -} + gcry_cipher_setiv ( hd, NULL, 0 ); + gcry_cipher_decrypt ( hd, seskey, slen, NULL, 0 ); + gcry_cipher_close ( hd ); + + /* Now we replace the dek components with the real session key to + decrypt the contents of the sequencing packet. */ + + dek->keylen=slen-1; + dek->algo=seskey[0]; + + if(dek->keylen > DIM(dek->key)) + BUG (); + + /* This is not completely accurate, since a bad passphrase may have + resulted in a garbage algorithm byte, but it's close enough since + a bogus byte here will fail later. */ + if(dek->algo==CIPHER_ALGO_IDEA) + idea_cipher_warn(0); + + memcpy(dek->key, seskey + 1, dek->keylen); + + /*log_hexdump( "thekey", dek->key, dek->keylen );*/ + + return 0; +} static void proc_symkey_enc( CTX c, PACKET *pkt ) @@ -284,26 +249,91 @@ proc_symkey_enc( CTX c, PACKET *pkt ) enc = pkt->pkt.symkey_enc; if (!enc) log_error ("invalid symkey encrypted packet\n"); - else { + else if(!c->dek) + { int algo = enc->cipher_algo; - const char *s; + const char *s = gcry_cipher_algo_name (algo); - s = gcry_cipher_algo_name (algo); - if (s && *s) - log_info(_("%s encrypted data\n"), s ); + if(s) + { + if(!opt.quiet) + { + if(enc->seskeylen) + log_info(_("%s encrypted session key\n"), s ); + else + log_info(_("%s encrypted data\n"), s ); + } + } else - log_info(_("encrypted with unknown algorithm %d\n"), algo ); + log_error(_("encrypted with unknown algorithm %d\n"), algo ); + + if(openpgp_md_test_algo (enc->s2k.hash_algo)) + { + log_error(_("passphrase generated with unknown digest" + " algorithm %d\n"),enc->s2k.hash_algo); + s=NULL; + } c->last_was_session_key = 2; - if ( opt.list_only ) - goto leave; - c->dek = passphrase_to_dek( NULL, 0, algo, &enc->s2k, 0, NULL, NULL ); - if (c->dek) - c->dek->algo_info_printed = 1; - if ( c->dek && enc->seskeylen ) - symkey_decrypt_sesskey( c->dek, enc->seskey, enc->seskeylen ); - } -leave: + if(!s || opt.list_only) + goto leave; + + if(opt.override_session_key) + { + c->dek = xmalloc_clear( sizeof *c->dek ); + if(get_override_session_key(c->dek, opt.override_session_key)) + { + xfree(c->dek); + c->dek = NULL; + } + } + else + { + int canceled; + + c->dek = passphrase_to_dek (NULL, 0, algo, &enc->s2k, 0, + NULL, &canceled); + if (canceled) + { + /* For unknown reasons passphrase_to_dek does only + return NULL if a new passphrase has been requested + and has not been repeated correctly. Thus even + with a cancel requested (by means of the gpg-agent) + it won't return NULL but an empty passphrase. We + take the most conservative approach for now and + work around it right here. */ + xfree (c->dek); + c->dek = NULL; + } + + if(c->dek) + { + c->dek->symmetric=1; + + /* FIXME: This doesn't work perfectly if a symmetric + key comes before a public key in the message - if + the user doesn't know the passphrase, then there is + a chance that the "decrypted" algorithm will happen + to be a valid one, which will make the returned dek + appear valid, so we won't try any public keys that + come later. */ + if(enc->seskeylen) + { + if(symkey_decrypt_seskey(c->dek, enc->seskey, + enc->seskeylen)) + { + xfree(c->dek); + c->dek=NULL; + } + } + else + c->dek->algo_info_printed = 1; + } + } + } + + leave: + c->symkeys++; free_packet(pkt); } @@ -320,7 +350,7 @@ proc_pubkey_enc( CTX c, PACKET *pkt ) /* Hmmm: why do I have this algo check here - anyway there is * function to check it. */ if( opt.verbose ) - log_info(_("public key is %08lX\n"), (ulong)enc->keyid[1] ); + log_info(_("public key is %s\n"), keystr(enc->keyid) ); if( is_status_enabled() ) { char buf[50]; @@ -332,62 +362,54 @@ proc_pubkey_enc( CTX c, PACKET *pkt ) if( !opt.list_only && opt.override_session_key ) { /* It does not make much sense to store the session key in * secure memory because it has already been passed on the - * command line and the GCHQ knows about it */ - c->dek = xcalloc (1, sizeof *c->dek ); + * command line and the GCHQ knows about it. */ + c->dek = xmalloc_clear( sizeof *c->dek ); result = get_override_session_key ( c->dek, opt.override_session_key ); if ( result ) { - xfree (c->dek); c->dek = NULL; + xfree(c->dek); c->dek = NULL; } } else if( is_ELGAMAL(enc->pubkey_algo) || enc->pubkey_algo == PUBKEY_ALGO_DSA || is_RSA(enc->pubkey_algo) ) { + /* FIXME: strore this all in a list and process it later */ + if ( !c->dek && ((!enc->keyid[0] && !enc->keyid[1]) || opt.try_all_secrets || !seckey_available( enc->keyid )) ) { if( opt.list_only ) result = -1; else { - c->dek = xcalloc_secure (1, sizeof *c->dek); + c->dek = xmalloc_secure_clear( sizeof *c->dek ); if( (result = get_session_key( enc, c->dek )) ) { /* error: delete the DEK */ - xfree (c->dek); c->dek = NULL; + xfree(c->dek); c->dek = NULL; } } } else - result = GPG_ERR_NO_SECKEY; + result = G10ERR_NO_SECKEY; } else - result = GPG_ERR_PUBKEY_ALGO; + result = G10ERR_PUBKEY_ALGO; if( result == -1 ) ; - else { - if( !result ) { - if( opt.verbose > 1 ) - log_info( _("public key encrypted data: good DEK\n") ); - if ( opt.show_session_key ) { - int i; - char *buf = xmalloc ( c->dek->keylen*2 + 20 ); - sprintf ( buf, "%d:", c->dek->algo ); - for(i=0; i < c->dek->keylen; i++ ) - sprintf(buf+strlen(buf), "%02X", c->dek->key[i] ); - log_info( "session key: \"%s\"\n", buf ); - write_status_text ( STATUS_SESSION_KEY, buf ); - } - } + else + { /* store it for later display */ - { - struct kidlist_item *x = xmalloc ( sizeof *x ); - x->kid[0] = enc->keyid[0]; - x->kid[1] = enc->keyid[1]; - x->pubkey_algo = enc->pubkey_algo; - x->reason = result; - x->next = c->pkenc_list; - c->pkenc_list = x; - } - } + struct kidlist_item *x = xmalloc( sizeof *x ); + x->kid[0] = enc->keyid[0]; + x->kid[1] = enc->keyid[1]; + x->pubkey_algo = enc->pubkey_algo; + x->reason = result; + x->next = c->pkenc_list; + c->pkenc_list = x; + + if( !result && opt.verbose > 1 ) + log_info( _("public key encrypted data: good DEK\n") ); + } + free_packet(pkt); } @@ -409,31 +431,29 @@ print_pkenc_list( struct kidlist_item *list, int failed ) if ( !failed && list->reason ) continue; - algstr = gcry_pk_algo_name (list->pubkey_algo); - pk = xcalloc (1, sizeof *pk ); + algstr = gcry_pk_algo_name ( list->pubkey_algo ); + pk = xmalloc_clear( sizeof *pk ); - if (!algstr || !*algstr) - algstr = "[?]"; + if( !algstr ) + algstr = "[?]"; pk->pubkey_algo = list->pubkey_algo; - if( !get_pubkey( pk, list->kid ) ) { - size_t n; + if( !get_pubkey( pk, list->kid ) ) + { char *p; - log_info( _("encrypted with %u-bit %s key, ID %08lX, created %s\n"), - nbits_from_pk( pk ), algstr, (ulong)list->kid[1], - strtimestamp(pk->timestamp) ); - fputs(" \"", log_get_stream() ); - p = get_user_id( list->kid, &n ); - print_utf8_string2 ( log_get_stream(), p, n, '"' ); - xfree (p); - fputs("\"\n", log_get_stream() ); - } - else { - log_info(_("encrypted with %s key, ID %08lX\n"), - algstr, (ulong) list->kid[1] ); - } + log_info( _("encrypted with %u-bit %s key, ID %s, created %s\n"), + nbits_from_pk( pk ), algstr, keystr_from_pk(pk), + strtimestamp(pk->timestamp) ); + p=get_user_id_native(list->kid); + fprintf(log_get_stream(),_(" \"%s\"\n"),p); + xfree(p); + } + else + log_info(_("encrypted with %s key, ID %s\n"), + algstr,keystr(list->kid)); + free_public_key( pk ); - if( gpg_err_code (list->reason) == GPG_ERR_NO_SECKEY ) { + if( list->reason == G10ERR_NO_SECKEY ) { if( is_status_enabled() ) { char buf[20]; sprintf(buf,"%08lX%08lX", (ulong)list->kid[0], @@ -443,7 +463,7 @@ print_pkenc_list( struct kidlist_item *list, int failed ) } else if (list->reason) log_info(_("public key decryption failed: %s\n"), - gpg_strerror (list->reason)); + g10_errstr(list->reason)); } } @@ -453,10 +473,18 @@ proc_encrypted( CTX c, PACKET *pkt ) { int result = 0; - if (!opt.quiet) { + if (!opt.quiet) + { + if(c->symkeys>1) + log_info(_("encrypted with %lu passphrases\n"),c->symkeys); + else if(c->symkeys==1) + log_info(_("encrypted with 1 passphrase\n")); print_pkenc_list ( c->pkenc_list, 1 ); print_pkenc_list ( c->pkenc_list, 0 ); - } + } + + /* FIXME: Figure out the session key by looking at all pkenc packets. */ + write_status( STATUS_BEGIN_DECRYPTION ); @@ -467,43 +495,58 @@ proc_encrypted( CTX c, PACKET *pkt ) int algo; STRING2KEY s2kbuf, *s2k = NULL; - /* assume this is old style conventional encrypted data */ - if ( (algo = opt.def_cipher_algo)) - log_info (_("assuming %s encrypted data\n"), + if(opt.override_session_key) + { + c->dek = xmalloc_clear( sizeof *c->dek ); + result=get_override_session_key(c->dek, opt.override_session_key); + if(result) + { + xfree(c->dek); + c->dek = NULL; + } + } + else + { + /* assume this is old style conventional encrypted data */ + if ( (algo = opt.def_cipher_algo)) + log_info (_("assuming %s encrypted data\n"), gcry_cipher_algo_name (algo)); - else if ( gcry_cipher_test_algo(CIPHER_ALGO_IDEA) ) { - algo = opt.def_cipher_algo; - if (!algo) - algo = opt.s2k_cipher_algo; - idea_cipher_warn(1); - log_info (_("IDEA cipher unavailable, " - "optimistically attempting to use %s instead\n"), - gcry_cipher_algo_name (algo)); - } - else { - algo = CIPHER_ALGO_IDEA; - if (!opt.s2k_digest_algo) { - /* If no digest is given we assume MD5 */ - s2kbuf.mode = 0; - s2kbuf.hash_algo = GCRY_MD_MD5; - s2k = &s2kbuf; - } - log_info (_("assuming %s encrypted data\n"), "IDEA"); - } + else if ( gcry_cipher_test_algo (CIPHER_ALGO_IDEA) ) + { + algo = opt.def_cipher_algo; + if (!algo) + algo = opt.s2k_cipher_algo; + idea_cipher_warn(1); + log_info (_("IDEA cipher unavailable, " + "optimistically attempting to use %s instead\n"), + gcry_cipher_algo_name (algo)); + } + else + { + algo = CIPHER_ALGO_IDEA; + if (!opt.s2k_digest_algo) + { + /* If no digest is given we assume MD5 */ + s2kbuf.mode = 0; + s2kbuf.hash_algo = DIGEST_ALGO_MD5; + s2k = &s2kbuf; + } + log_info (_("assuming %s encrypted data\n"), "IDEA"); + } - c->dek = passphrase_to_dek ( NULL, 0, algo, s2k, 0, NULL, NULL ); - if (c->dek) - c->dek->algo_info_printed = 1; + c->dek = passphrase_to_dek ( NULL, 0, algo, s2k, 0, NULL, NULL ); + if (c->dek) + c->dek->algo_info_printed = 1; + } } else if( !c->dek ) - result = GPG_ERR_NO_SECKEY; + result = G10ERR_NO_SECKEY; if( !result ) result = decrypt_data( c, pkt->pkt.encrypted, c->dek ); - xfree (c->dek); c->dek = NULL; if( result == -1 ) ; - else if( !result || (gpg_err_code (result)==GPG_ERR_BAD_SIGNATURE + else if( !result || (gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE && opt.ignore_mdc_error)) { write_status( STATUS_DECRYPTION_OKAY ); if( opt.verbose > 1 ) @@ -512,25 +555,35 @@ proc_encrypted( CTX c, PACKET *pkt ) write_status( STATUS_GOODMDC ); else if(!opt.no_mdc_warn) log_info (_("WARNING: message was not integrity protected\n")); + if(opt.show_session_key) + { + int i; + char *buf = xmalloc ( c->dek->keylen*2 + 20 ); + sprintf ( buf, "%d:", c->dek->algo ); + for(i=0; i < c->dek->keylen; i++ ) + sprintf(buf+strlen(buf), "%02X", c->dek->key[i] ); + log_info( "session key: `%s'\n", buf ); + write_status_text ( STATUS_SESSION_KEY, buf ); + } } - else if( gpg_err_code (result) == GPG_ERR_BAD_SIGNATURE ) { + else if( result == G10ERR_BAD_SIGN ) { log_error(_("WARNING: encrypted message has been manipulated!\n")); write_status( STATUS_BADMDC ); write_status( STATUS_DECRYPTION_FAILED ); } else { write_status( STATUS_DECRYPTION_FAILED ); - log_error(_("decryption failed: %s\n"), gpg_strerror (result)); + log_error(_("decryption failed: %s\n"), g10_errstr(result)); /* Hmmm: does this work when we have encrypted using multiple * ways to specify the session key (symmmetric and PK)*/ } + xfree(c->dek); c->dek = NULL; free_packet(pkt); c->last_was_session_key = 0; write_status( STATUS_END_DECRYPTION ); } - static void proc_plaintext( CTX c, PACKET *pkt ) { @@ -543,7 +596,8 @@ proc_plaintext( CTX c, PACKET *pkt ) else if( opt.verbose ) log_info(_("original file name='%.*s'\n"), pt->namelen, pt->name); free_md_filter_context( &c->mfx ); - gcry_md_open (&c->mfx.md, 0, 0); + if (gcry_md_open (&c->mfx.md, 0, 0)) + BUG (); /* fixme: we may need to push the textfilter if we have sigclass 1 * and no armoring - Not yet tested * Hmmm, why don't we need it at all if we have sigclass 1 @@ -551,72 +605,90 @@ proc_plaintext( CTX c, PACKET *pkt ) * See: Russ Allbery's mail 1999-02-09 */ any = clearsig = only_md5 = 0; - for(n=c->list; n; n = n->next ) { - if( n->pkt->pkttype == PKT_ONEPASS_SIG ) { - if( n->pkt->pkt.onepass_sig->digest_algo ) { - gcry_md_enable ( c->mfx.md, n->pkt->pkt.onepass_sig->digest_algo ); + for(n=c->list; n; n = n->next ) + { + if( n->pkt->pkttype == PKT_ONEPASS_SIG ) + { + /* For the onepass signature case */ + if( n->pkt->pkt.onepass_sig->digest_algo ) + { + gcry_md_enable (c->mfx.md, + n->pkt->pkt.onepass_sig->digest_algo); if( !any && n->pkt->pkt.onepass_sig->digest_algo - == DIGEST_ALGO_MD5 ) - only_md5 = 1; + == DIGEST_ALGO_MD5 ) + only_md5 = 1; else - only_md5 = 0; + only_md5 = 0; any = 1; - } + } if( n->pkt->pkt.onepass_sig->sig_class != 0x01 ) - only_md5 = 0; - } + only_md5 = 0; + } else if( n->pkt->pkttype == PKT_GPG_CONTROL && n->pkt->pkt.gpg_control->control - == CTRLPKT_CLEARSIGN_START ) { + == CTRLPKT_CLEARSIGN_START ) + { + /* For the clearsigned message case */ size_t datalen = n->pkt->pkt.gpg_control->datalen; const byte *data = n->pkt->pkt.gpg_control->data; /* check that we have at least the sigclass and one hash */ if ( datalen < 2 ) - log_fatal("invalid control packet CTRLPKT_CLEARSIGN_START\n"); + log_fatal("invalid control packet CTRLPKT_CLEARSIGN_START\n"); /* Note that we don't set the clearsig flag for not-dash-escaped * documents */ clearsig = (*data == 0x01); for( data++, datalen--; datalen; datalen--, data++ ) - gcry_md_enable ( c->mfx.md, *data ); + md_enable( c->mfx.md, *data ); any = 1; - break; /* no pass signature pakets are expected */ - } - } + break; /* Stop here as one-pass signature packets are not + expected. */ + } + else if(n->pkt->pkttype==PKT_SIGNATURE) + { + /* For the SIG+LITERAL case that PGP used to use. */ + md_enable( c->mfx.md, n->pkt->pkt.signature->digest_algo ); + any=1; + } + } - if( !any && !opt.skip_verify ) { - /* no onepass sig packet: enable all standard algos */ - gcry_md_enable ( c->mfx.md, DIGEST_ALGO_RMD160 ); - gcry_md_enable ( c->mfx.md, DIGEST_ALGO_SHA1 ); - gcry_md_enable ( c->mfx.md, DIGEST_ALGO_MD5 ); - } + if( !any && !opt.skip_verify ) + { + /* This is for the old GPG LITERAL+SIG case. It's not legal + according to 2440, so hopefully it won't come up that + often. There is no good way to specify what algorithms to + use in that case, so these three are the historical + answer. */ + md_enable( c->mfx.md, DIGEST_ALGO_RMD160 ); + md_enable( c->mfx.md, DIGEST_ALGO_SHA1 ); + md_enable( c->mfx.md, DIGEST_ALGO_MD5 ); + } if( opt.pgp2_workarounds && only_md5 && !opt.skip_verify ) { /* This is a kludge to work around a bug in pgp2. It does only * catch those mails which are armored. To catch the non-armored * pgp mails we could see whether there is the signature packet * in front of the plaintext. If someone needs this, send me a patch. */ - gcry_md_open (&c->mfx.md2, DIGEST_ALGO_MD5, 0); + if ( gcry_md_open (&c->mfx.md2, DIGEST_ALGO_MD5, 0) ) + BUG (); } if ( DBG_HASHING ) { gcry_md_start_debug ( c->mfx.md, "verify" ); if ( c->mfx.md2 ) gcry_md_start_debug ( c->mfx.md2, "verify2" ); } - if ( c->pipemode.op == 'B' ) - rc = handle_plaintext( pt, &c->mfx, 1, 0, NULL ); - else { - int failed; - rc = handle_plaintext( pt, &c->mfx, c->sigs_only, clearsig, &failed); - if( rc && failed && !c->sigs_only) { - /* can't write output but we hash it anyway to - * check the signature */ - rc = handle_plaintext( pt, &c->mfx, 1, clearsig, NULL ); - } - } + rc = handle_plaintext( pt, &c->mfx, c->sigs_only, clearsig ); + if( gpg_err_code (rc) == G10ERR_CREATE_FILE && !c->sigs_only) + { +#warning We need to change the test for the error code + /* Can't write output but we hash it anyway to + * Check the signature. */ + rc = handle_plaintext( pt, &c->mfx, 1, clearsig ); + } + if( rc ) - log_error( "handle plaintext failed: %s\n", gpg_strerror (rc)); + log_error( "handle plaintext failed: %s\n", g10_errstr(rc)); free_packet(pkt); c->last_was_session_key = 0; @@ -632,14 +704,14 @@ proc_plaintext( CTX c, PACKET *pkt ) static int -proc_compressed_cb( iobuf_t a, void *info ) +proc_compressed_cb( IOBUF a, void *info ) { return proc_signature_packets( info, a, ((CTX)info)->signed_data, ((CTX)info)->sigfilename ); } static int -proc_encrypt_cb( iobuf_t a, void *info ) +proc_encrypt_cb( IOBUF a, void *info ) { return proc_encryption_packets( info, a ); } @@ -651,14 +723,16 @@ proc_compressed( CTX c, PACKET *pkt ) int rc; /*printf("zip: compressed data packet\n");*/ - if( c->sigs_only ) + if( !zd->algorithm ) + rc=G10ERR_COMPR_ALGO; + else if( c->sigs_only ) rc = handle_compressed( c, zd, proc_compressed_cb, c ); else if( c->encrypt_only ) rc = handle_compressed( c, zd, proc_encrypt_cb, c ); else rc = handle_compressed( c, zd, NULL, NULL ); if( rc ) - log_error("uncompressing failed: %s\n", gpg_strerror (rc)); + log_error("uncompressing failed: %s\n", g10_errstr(rc)); free_packet(pkt); c->last_was_session_key = 0; } @@ -669,10 +743,10 @@ proc_compressed( CTX c, PACKET *pkt ) */ static int do_check_sig( CTX c, KBNODE node, int *is_selfsig, - int *is_expkey, int *is_revkey ) + int *is_expkey, int *is_revkey ) { PKT_signature *sig; - MD_HANDLE md = NULL, md2 = NULL; + gcry_md_hd_t md = NULL, md2 = NULL; int algo, rc; assert( node->pkt->pkttype == PKT_SIGNATURE ); @@ -681,29 +755,39 @@ do_check_sig( CTX c, KBNODE node, int *is_selfsig, sig = node->pkt->pkt.signature; algo = sig->digest_algo; - if( (rc = gcry_md_test_algo(algo)) ) - return rc; + rc = openpgp_md_test_algo(algo); + if (rc) + return rc; if( sig->sig_class == 0x00 ) { if( c->mfx.md ) - gcry_md_copy (&md,c->mfx.md); + { + if (gcry_md_copy (&md, c->mfx.md )) + BUG (); + } else /* detached signature */ - gcry_md_open (&md, 0, 0 ); /* signature_check() will - enable the md*/ + { + /* signature_check() will enable the md*/ + if (gcry_md_open (&md, 0, 0 )) + BUG (); + } } else if( sig->sig_class == 0x01 ) { /* how do we know that we have to hash the (already hashed) text * in canonical mode ??? (calculating both modes???) */ if( c->mfx.md ) { - gcry_md_copy (&md, c->mfx.md); - if (c->mfx.md2) - gcry_md_copy (&md2, c->mfx.md2); + if (gcry_md_copy (&md, c->mfx.md )) + BUG (); + if( c->mfx.md2 && gcry_md_copy (&md2, c->mfx.md2 )) + BUG (); } else { /* detached signature */ - log_debug("Do we really need this here?"); - gcry_md_open (&md, 0, 0 ); /* signature_check() will - enable the md*/ - gcry_md_open (&md2, 0, 0 ); + log_debug("Do we really need this here?"); + /* signature_check() will enable the md*/ + if (gcry_md_open (&md, 0, 0 )) + BUG (); + if (gcry_md_open (&md2, 0, 0 )) + BUG (); } } else if( (sig->sig_class&~3) == 0x10 @@ -717,23 +801,23 @@ do_check_sig( CTX c, KBNODE node, int *is_selfsig, return check_key_signature( c->list, node, is_selfsig ); } else if( sig->sig_class == 0x20 ) { - log_info(_("standalone revocation - " - "use \"gpg --import\" to apply\n")); - return GPG_ERR_NOT_PROCESSED; + log_error (_("standalone revocation - " + "use \"gpg --import\" to apply\n")); + return G10ERR_NOT_PROCESSED; } else { log_error("invalid root packet for sigclass %02x\n", sig->sig_class); - return GPG_ERR_SIG_CLASS; + return G10ERR_SIG_CLASS; } } else - return GPG_ERR_SIG_CLASS; + return G10ERR_SIG_CLASS; rc = signature_check2( sig, md, NULL, is_expkey, is_revkey, NULL ); if( gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE && md2 ) rc = signature_check2( sig, md2, NULL, is_expkey, is_revkey, NULL ); - gcry_md_close (md); - gcry_md_close (md2); + gcry_md_close(md); + gcry_md_close(md2); return rc; } @@ -780,44 +864,38 @@ list_node( CTX c, KBNODE node ) || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { PKT_public_key *pk = node->pkt->pkt.public_key; - if( opt.with_colons ) { + if( opt.with_colons ) + { u32 keyid[2]; keyid_from_pk( pk, keyid ); - if( mainkey ) { - c->local_id = pk->local_id; - c->trustletter = opt.fast_list_mode? - 0 : get_validity_info( pk, NULL ); - } + if( mainkey ) + c->trustletter = opt.fast_list_mode? + 0 : get_validity_info( pk, NULL ); printf("%s:", mainkey? "pub":"sub" ); if( c->trustletter ) - putchar( c->trustletter ); - printf(":%u:%d:%08lX%08lX:%s:%s:", - nbits_from_pk( pk ), - pk->pubkey_algo, - (ulong)keyid[0],(ulong)keyid[1], - colon_datestr_from_pk( pk ), - colon_strtime (pk->expiredate) ); - if( c->local_id ) - printf("%lu", c->local_id ); - putchar(':'); + putchar( c->trustletter ); + printf(":%u:%d:%08lX%08lX:%s:%s::", + nbits_from_pk( pk ), + pk->pubkey_algo, + (ulong)keyid[0],(ulong)keyid[1], + colon_datestr_from_pk( pk ), + colon_strtime (pk->expiredate) ); if( mainkey && !opt.fast_list_mode ) - putchar( get_ownertrust_info (pk) ); + putchar( get_ownertrust_info (pk) ); putchar(':'); if( node->next && node->next->pkt->pkttype == PKT_RING_TRUST) { - putchar('\n'); any=1; - if( opt.fingerprint ) - print_fingerprint( pk, NULL, 0 ); - printf("rtv:1:%u:\n", - node->next->pkt->pkt.ring_trust->trustval ); + putchar('\n'); any=1; + if( opt.fingerprint ) + print_fingerprint( pk, NULL, 0 ); + printf("rtv:1:%u:\n", + node->next->pkt->pkt.ring_trust->trustval ); } - } + } else - printf("%s %4u%c/%08lX %s ", - mainkey? "pub":"sub", - nbits_from_pk( pk ), - pubkey_letter( pk->pubkey_algo ), - (ulong)keyid_from_pk( pk, NULL ), - datestr_from_pk( pk ) ); + printf("%s %4u%c/%s %s%s", + mainkey? "pub":"sub", nbits_from_pk( pk ), + pubkey_letter( pk->pubkey_algo ), keystr_from_pk( pk ), + datestr_from_pk( pk ), mainkey?" ":""); if( mainkey ) { /* and now list all userids with their signatures */ @@ -846,10 +924,12 @@ list_node( CTX c, KBNODE node ) putchar('\n'); if( opt.fingerprint && !any ) print_fingerprint( pk, NULL, 0 ); - if( node->next + if( opt.with_colons + && node->next && node->next->pkt->pkttype == PKT_RING_TRUST ) { printf("rtv:2:%u:\n", - node->next->pkt->pkt.ring_trust->trustval ); + node->next->pkt->pkt.ring_trust? + node->next->pkt->pkt.ring_trust->trustval : 0); } any=1; } @@ -862,9 +942,22 @@ list_node( CTX c, KBNODE node ) } } } - else if( pk->expiredate ) { /* of subkey */ - printf(_(" [expires: %s]"), expirestr_from_pk( pk ) ); - } + else + { + /* of subkey */ + if( pk->is_revoked ) + { + printf(" ["); + printf(_("revoked: %s"),revokestr_from_pk(pk)); + printf("]"); + } + else if( pk->expiredate ) + { + printf(" ["); + printf(_("expires: %s"),expirestr_from_pk(pk)); + printf("]"); + } + } if( !any ) putchar('\n'); @@ -875,25 +968,23 @@ list_node( CTX c, KBNODE node ) || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { PKT_secret_key *sk = node->pkt->pkt.secret_key; - if( opt.with_colons ) { + if( opt.with_colons ) + { u32 keyid[2]; keyid_from_sk( sk, keyid ); printf("%s::%u:%d:%08lX%08lX:%s:%s:::", - mainkey? "sec":"ssb", - nbits_from_sk( sk ), - sk->pubkey_algo, - (ulong)keyid[0],(ulong)keyid[1], - colon_datestr_from_sk( sk ), - colon_strtime (sk->expiredate) - /* fixme: add LID */ ); - } + mainkey? "sec":"ssb", + nbits_from_sk( sk ), + sk->pubkey_algo, + (ulong)keyid[0],(ulong)keyid[1], + colon_datestr_from_sk( sk ), + colon_strtime (sk->expiredate) + /* fixme: add LID */ ); + } else - printf("%s %4u%c/%08lX %s ", - mainkey? "sec":"ssb", - nbits_from_sk( sk ), - pubkey_letter( sk->pubkey_algo ), - (ulong)keyid_from_sk( sk, NULL ), - datestr_from_sk( sk ) ); + printf("%s %4u%c/%s %s ", mainkey? "sec":"ssb", + nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ), + keystr_from_sk( sk ), datestr_from_sk( sk )); if( mainkey ) { /* and now list all userids with their signatures */ for( node = node->next; node; node = node->next ) { @@ -945,7 +1036,7 @@ list_node( CTX c, KBNODE node ) char *p; int sigrc = ' '; - if( !opt.list_sigs ) + if( !opt.verbose ) return; if( sig->sig_class == 0x20 || sig->sig_class == 0x30 ) @@ -954,14 +1045,13 @@ list_node( CTX c, KBNODE node ) fputs("sig", stdout); if( opt.check_sigs ) { fflush(stdout); - switch( gpg_err_code (rc2=do_check_sig( c, node, - &is_selfsig, - NULL, NULL )) ) { - case 0: sigrc = '!'; break; - case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; + rc2=do_check_sig( c, node, &is_selfsig, NULL, NULL ); + switch (gpg_err_code (rc2)) { + case 0: sigrc = '!'; break; + case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: - case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; - default: sigrc = '%'; break; + case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; + default: sigrc = '%'; break; } } else { /* check whether this is a self signature */ @@ -997,10 +1087,10 @@ list_node( CTX c, KBNODE node ) printf(":"); } else - printf("%c %08lX %s ", - sigrc, (ulong)sig->keyid[1], datestr_from_sig(sig)); + printf("%c %s %s ", + sigrc, keystr(sig->keyid), datestr_from_sig(sig)); if( sigrc == '%' ) - printf("[%s] ", gpg_strerror (rc2) ); + printf("[%s] ", g10_errstr(rc2) ); else if( sigrc == '?' ) ; else if( is_selfsig ) { @@ -1013,7 +1103,7 @@ list_node( CTX c, KBNODE node ) else if( !opt.fast_list_mode ) { p = get_user_id( sig->keyid, &n ); print_string( stdout, p, n, opt.with_colons ); - xfree (p); + xfree(p); } if( opt.with_colons ) printf(":%02x%c:", sig->sig_class, sig->flags.exportable?'x':'l'); @@ -1026,24 +1116,24 @@ list_node( CTX c, KBNODE node ) int -proc_packets( void *anchor, iobuf_t a ) +proc_packets( void *anchor, IOBUF a ) { int rc; - CTX c = xcalloc (1, sizeof *c ); + CTX c = xmalloc_clear( sizeof *c ); c->anchor = anchor; rc = do_proc_packets( c, a ); - xfree ( c ); + xfree( c ); return rc; } int -proc_signature_packets( void *anchor, iobuf_t a, +proc_signature_packets( void *anchor, IOBUF a, STRLIST signedfiles, const char *sigfilename ) { - CTX c = xcalloc (1, sizeof *c ); + CTX c = xmalloc_clear( sizeof *c ); int rc; c->anchor = anchor; @@ -1051,28 +1141,47 @@ proc_signature_packets( void *anchor, iobuf_t a, c->signed_data = signedfiles; c->sigfilename = sigfilename; rc = do_proc_packets( c, a ); - xfree ( c ); + + /* If we have not encountered any signature we print an error + messages, send a NODATA status back and return an error code. + Using log_error is required because verify_files does not check + error codes for each file but we want to terminate the process + with an error. */ + if (!rc && !c->any_sig_seen) + { + write_status_text (STATUS_NODATA, "4"); + log_error (_("no signature found\n")); + rc = G10ERR_NO_DATA; + } + + /* Propagate the signature seen flag upward. Do this only on + success so that we won't issue the nodata status several + times. */ + if (!rc && c->anchor && c->any_sig_seen) + c->anchor->any_sig_seen = 1; + + xfree( c ); return rc; } int -proc_encryption_packets( void *anchor, iobuf_t a ) +proc_encryption_packets( void *anchor, IOBUF a ) { - CTX c = xcalloc (1, sizeof *c ); + CTX c = xmalloc_clear( sizeof *c ); int rc; c->anchor = anchor; c->encrypt_only = 1; rc = do_proc_packets( c, a ); - xfree ( c ); + xfree( c ); return rc; } int -do_proc_packets( CTX c, iobuf_t a ) +do_proc_packets( CTX c, IOBUF a ) { - PACKET *pkt = xmalloc ( sizeof *pkt ); + PACKET *pkt = xmalloc( sizeof *pkt ); int rc=0; int any_data=0; int newpkt; @@ -1084,8 +1193,9 @@ do_proc_packets( CTX c, iobuf_t a ) if( rc ) { free_packet(pkt); /* stop processing when an invalid packet has been encountered - * but don't do so when we are doing a --list-packet. */ - if( gpg_err_code (rc) == GPG_ERR_INV_PACKET && opt.list_packets != 2 ) + * but don't do so when we are doing a --list-packets. */ + if (gpg_err_code (rc) == GPG_ERR_INVALID_PACKET + && opt.list_packets != 2 ) break; continue; } @@ -1110,7 +1220,7 @@ do_proc_packets( CTX c, iobuf_t a ) case PKT_ENCRYPTED: case PKT_ENCRYPTED_MDC: write_status_text( STATUS_UNEXPECTED, "0" ); - rc = GPG_ERR_UNEXPECTED; + rc = G10ERR_UNEXPECTED; goto leave; case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break; case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break; @@ -1126,7 +1236,7 @@ do_proc_packets( CTX c, iobuf_t a ) case PKT_SECRET_KEY: case PKT_USER_ID: write_status_text( STATUS_UNEXPECTED, "0" ); - rc = GPG_ERR_UNEXPECTED; + rc = G10ERR_UNEXPECTED; goto leave; case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break; case PKT_SYMKEY_ENC: proc_symkey_enc( c, pkt ); break; @@ -1180,19 +1290,13 @@ do_proc_packets( CTX c, iobuf_t a ) if( newpkt == -1 ) ; else if( newpkt ) { - pkt = xmalloc ( sizeof *pkt ); + pkt = xmalloc( sizeof *pkt ); init_packet(pkt); } else free_packet(pkt); - if ( c->pipemode.stop_now ) { - /* we won't get an EOF in pipemode, so we have to - * break the loop here */ - rc = -1; - break; - } } - if( gpg_err_code (rc) == GPG_ERR_INV_PACKET ) + if( rc == G10ERR_INVALID_PACKET ) write_status_text( STATUS_NODATA, "3" ); if( any_data ) rc = 0; @@ -1202,105 +1306,213 @@ do_proc_packets( CTX c, iobuf_t a ) leave: release_list( c ); - xfree (c->dek); + xfree(c->dek); free_packet( pkt ); - xfree ( pkt ); + xfree( pkt ); free_md_filter_context( &c->mfx ); return rc; } -static int -check_sig_and_print( CTX c, KBNODE node ) +/* Helper for pka_uri_from_sig to parse the to-be-verified address out + of the notation data. */ +static pka_info_t * +get_pka_address (PKT_signature *sig) { - PKT_signature *sig = node->pkt->pkt.signature; - const char *astr, *tstr; - int rc, is_expkey=0, is_revkey=0; + pka_info_t *pka = NULL; + struct notation *nd,*notation; - if( opt.skip_verify ) { - log_info(_("signature verification suppressed\n")); - return 0; - } + notation=sig_to_notation(sig); - /* It is not in all cases possible to check multiple signatures: - * PGP 2 (which is also allowed by OpenPGP), does use the packet - * sequence: sig+data, OpenPGP does use onepas+data=sig and GnuPG - * sometimes uses (because I did'nt read the specs right) data+sig. - * Because it is possible to create multiple signatures with - * different packet sequence (e.g. data+sig and sig+data) it might - * not be possible to get it right: let's say we have: - * data+sig, sig+data,sig+data and we have not yet encountered the last - * data, we could also see this a one data with 2 signatures and then - * data+sig. - * To protect against this we check that all signatures follow - * without any intermediate packets. Note, that we won't get this - * error when we use onepass packets or cleartext signatures because - * we reset the list every time - * - * FIXME: Now that we have these marker packets, we should create a - * real grammar and check against this. - */ + for(nd=notation;nd;nd=nd->next) { - KBNODE n; - int n_sig=0; + if(strcmp(nd->name,"pka-address@gnupg.org")!=0) + continue; /* Not the notation we want. */ + + /* For now we only use the first valid PKA notation. In future + we might want to keep additional PKA notations in a linked + list. */ + if (is_valid_mailbox (nd->value)) + { + pka = xmalloc (sizeof *pka + strlen(nd->value)); + pka->valid = 0; + pka->checked = 0; + pka->uri = NULL; + strcpy (pka->email, nd->value); + break; + } + } - for (n=c->list; n; n=n->next ) { - if ( n->pkt->pkttype == PKT_SIGNATURE ) - n_sig++; - } - if (n_sig > 1) { /* more than one signature - check sequence */ - int tmp, onepass; - - for (tmp=onepass=0,n=c->list; n; n=n->next ) { - if (n->pkt->pkttype == PKT_ONEPASS_SIG) - onepass++; - else if (n->pkt->pkttype == PKT_GPG_CONTROL - && n->pkt->pkt.gpg_control->control - == CTRLPKT_CLEARSIGN_START ) { - onepass++; /* handle the same way as a onepass */ - } - else if ( (tmp && n->pkt->pkttype != PKT_SIGNATURE) ) { - log_error(_("can't handle these multiple signatures\n")); - return 0; - } - else if ( n->pkt->pkttype == PKT_SIGNATURE ) - tmp = 1; - else if (!tmp && !onepass - && n->pkt->pkttype == PKT_GPG_CONTROL - && n->pkt->pkt.gpg_control->control - == CTRLPKT_PLAINTEXT_MARK ) { - /* plaintext before signatures but no one-pass packets*/ - log_error(_("can't handle these multiple signatures\n")); - return 0; - } + free_notation(notation); + + return pka; +} + + +/* Return the URI from a DNS PKA record. If this record has already + be retrieved for the signature we merely return it; if not we go + out and try to get that DNS record. */ +static const char * +pka_uri_from_sig (PKT_signature *sig) +{ + if (!sig->flags.pka_tried) + { + assert (!sig->pka_info); + sig->flags.pka_tried = 1; + sig->pka_info = get_pka_address (sig); + if (sig->pka_info) + { + char *uri; + + uri = get_pka_info (sig->pka_info->email, sig->pka_info->fpr); + if (uri) + { + sig->pka_info->valid = 1; + if (!*uri) + xfree (uri); + else + sig->pka_info->uri = uri; } } } + return sig->pka_info? sig->pka_info->uri : NULL; +} + + +static int +check_sig_and_print( CTX c, KBNODE node ) +{ + PKT_signature *sig = node->pkt->pkt.signature; + const char *astr; + int rc, is_expkey=0, is_revkey=0; + + if (opt.skip_verify) + { + log_info(_("signature verification suppressed\n")); + return 0; + } + + /* Check that the message composition is valid. + + Per RFC-2440bis (-15) allowed: + + S{1,n} -- detached signature. + S{1,n} P -- old style PGP2 signature + O{1,n} P S{1,n} -- standard OpenPGP signature. + C P S{1,n} -- cleartext signature. + + + O = One-Pass Signature packet. + S = Signature packet. + P = OpenPGP Message packet (Encrypted | Compressed | Literal) + (Note that the current rfc2440bis draft also allows + for a signed message but that does not work as it + introduces ambiguities.) + We keep track of these packages using the marker packet + CTRLPKT_PLAINTEXT_MARK. + C = Marker packet for cleartext signatures. + + We reject all other messages. + + Actually we are calling this too often, i.e. for verification of + each message but better have some duplicate work than to silently + introduce a bug here. + */ + { + KBNODE n; + int n_onepass, n_sig; + +/* log_debug ("checking signature packet composition\n"); */ +/* dump_kbnode (c->list); */ + + n = c->list; + assert (n); + if ( n->pkt->pkttype == PKT_SIGNATURE ) + { + /* This is either "S{1,n}" case (detached signature) or + "S{1,n} P" (old style PGP2 signature). */ + for (n = n->next; n; n = n->next) + if (n->pkt->pkttype != PKT_SIGNATURE) + break; + if (!n) + ; /* Okay, this is a detached signature. */ + else if (n->pkt->pkttype == PKT_GPG_CONTROL + && (n->pkt->pkt.gpg_control->control + == CTRLPKT_PLAINTEXT_MARK) ) + { + if (n->next) + goto ambiguous; /* We only allow one P packet. */ + } + else + goto ambiguous; + } + else if (n->pkt->pkttype == PKT_ONEPASS_SIG) + { + /* This is the "O{1,n} P S{1,n}" case (standard signature). */ + for (n_onepass=1, n = n->next; + n && n->pkt->pkttype == PKT_ONEPASS_SIG; n = n->next) + n_onepass++; + if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL + && (n->pkt->pkt.gpg_control->control + == CTRLPKT_PLAINTEXT_MARK))) + goto ambiguous; + for (n_sig=0, n = n->next; + n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) + n_sig++; + if (!n_sig) + goto ambiguous; + if (n && !opt.allow_multisig_verification) + goto ambiguous; + if (n_onepass != n_sig) + { + log_info ("number of one-pass packets does not match " + "number of signature packets\n"); + goto ambiguous; + } + } + else if (n->pkt->pkttype == PKT_GPG_CONTROL + && n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START ) + { + /* This is the "C P S{1,n}" case (clear text signature). */ + n = n->next; + if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL + && (n->pkt->pkt.gpg_control->control + == CTRLPKT_PLAINTEXT_MARK))) + goto ambiguous; + for (n_sig=0, n = n->next; + n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) + n_sig++; + if (n || !n_sig) + goto ambiguous; + } + else + { + ambiguous: + log_error(_("can't handle this ambiguous signature data\n")); + return 0; + } + + } + + /* (Indendation below not yet changed to GNU style.) */ - tstr = asctimestamp(sig->timestamp); - astr = gcry_pk_algo_name (sig->pubkey_algo); - if(opt.verify_options&VERIFY_SHOW_LONG_KEYID) + astr = gcry_pk_algo_name ( sig->pubkey_algo ); + if(keystrlen()>8) { - log_info(_("Signature made %.*s\n"),(int)strlen(tstr), tstr); - log_info(_(" using %s key %08lX%08lX\n"), - astr? astr: "?",(ulong)sig->keyid[0],(ulong)sig->keyid[1] ); + log_info(_("Signature made %s\n"),asctimestamp(sig->timestamp)); + log_info(_(" using %s key %s\n"), + astr? astr: "?",keystr(sig->keyid)); } else - log_info(_("Signature made %.*s using %s key ID %08lX\n"), - (int)strlen(tstr), tstr, astr? astr: "?", - (ulong)sig->keyid[1] ); + log_info(_("Signature made %s using %s key ID %s\n"), + asctimestamp(sig->timestamp), astr? astr: "?", + keystr(sig->keyid)); rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey ); - if( gpg_err_code (rc) == GPG_ERR_NO_PUBKEY - && opt.keyserver_scheme && opt.keyserver_options.auto_key_retrieve) { - if( keyserver_import_keyid ( sig->keyid )==0 ) - rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey ); - } + /* If the key isn't found, check for a preferred keyserver */ - /* If the key still isn't found, try to inform the user where it - can be found. */ - if(gpg_err_code (rc)==GPG_ERR_NO_PUBKEY && sig->flags.pref_ks) + if(rc==G10ERR_NO_PUBKEY && sig->flags.pref_ks) { const byte *p; int seq=0; @@ -1313,17 +1525,81 @@ check_sig_and_print( CTX c, KBNODE node ) page, but "from" if it is located on a keyserver. I'm not going to even try to make two strings here :) */ log_info(_("Key available at: ") ); - print_string( log_get_stream(), p, n, 0 ); + print_utf8_string( log_get_stream(), p, n ); putc( '\n', log_get_stream() ); + + if(opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE + && opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL) + { + struct keyserver_spec *spec; + + spec=parse_preferred_keyserver(sig); + if(spec) + { + int res; + + glo_ctrl.in_auto_key_retrieve++; + res=keyserver_import_keyid(sig->keyid,spec); + glo_ctrl.in_auto_key_retrieve--; + if(!res) + rc=do_check_sig(c, node, NULL, &is_expkey, &is_revkey ); + free_keyserver_spec(spec); + + if(!rc) + break; + } + } } } + /* If the preferred keyserver thing above didn't work, our second + try is to use the URI from a DNS PKA record. */ + if ( rc == G10ERR_NO_PUBKEY + && opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE + && opt.keyserver_options.options&KEYSERVER_HONOR_PKA_RECORD) + { + const char *uri = pka_uri_from_sig (sig); + + if (uri) + { + /* FIXME: We might want to locate the key using the + fingerprint instead of the keyid. */ + int res; + struct keyserver_spec *spec; + + spec = parse_keyserver_uri (uri, 1, NULL, 0); + if (spec) + { + glo_ctrl.in_auto_key_retrieve++; + res = keyserver_import_keyid (sig->keyid, spec); + glo_ctrl.in_auto_key_retrieve--; + free_keyserver_spec (spec); + if (!res) + rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey ); + } + } + } + + /* If the preferred keyserver thing above didn't work and we got + no information from the DNS PKA, this is a third try. */ + + if( rc == G10ERR_NO_PUBKEY && opt.keyserver + && opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE) + { + int res; + + glo_ctrl.in_auto_key_retrieve++; + res=keyserver_import_keyid ( sig->keyid, opt.keyserver ); + glo_ctrl.in_auto_key_retrieve--; + if(!res) + rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey ); + } if( !rc || gpg_err_code (rc) == GPG_ERR_BAD_SIGNATURE ) { KBNODE un, keyblock; int count=0, statno; char keyid_str[50]; - PKT_public_key *pk=NULL; + PKT_public_key *pk=NULL; if(rc) statno=STATUS_BADSIG; @@ -1343,8 +1619,8 @@ check_sig_and_print( CTX c, KBNODE node ) /* find and print the primary user ID */ for( un=keyblock; un; un = un->next ) { + char *p; int valid; - if(un->pkt->pkttype==PKT_PUBLIC_KEY) { pk=un->pkt->pkt.public_key; @@ -1360,10 +1636,10 @@ check_sig_and_print( CTX c, KBNODE node ) continue; if ( !un->pkt->pkt.user_id->is_primary ) continue; - /* We want the textual user ID here */ + /* We want the textual primary user ID here */ if ( un->pkt->pkt.user_id->attrib_data ) continue; - + assert(pk); /* Get it before we print anything to avoid interrupting @@ -1377,20 +1653,28 @@ check_sig_and_print( CTX c, KBNODE node ) un->pkt->pkt.user_id->len, -1 ); - log_info(rc? _("BAD signature from \"") - : sig->flags.expired ? _("Expired signature from \"") - : _("Good signature from \"")); - print_utf8_string( log_get_stream(), un->pkt->pkt.user_id->name, - un->pkt->pkt.user_id->len ); - if(opt.verify_options&VERIFY_SHOW_VALIDITY) - fprintf (log_get_stream(), - "\" [%s]\n",trust_value_to_string(valid)); + p=utf8_to_native(un->pkt->pkt.user_id->name, + un->pkt->pkt.user_id->len,0); + + if(rc) + log_info(_("BAD signature from \"%s\""),p); + else if(sig->flags.expired) + log_info(_("Expired signature from \"%s\""),p); + else + log_info(_("Good signature from \"%s\""),p); + + xfree(p); + + if(opt.verify_options&VERIFY_SHOW_UID_VALIDITY) + fprintf(log_get_stream()," [%s]\n",trust_value_to_string(valid)); else - fputs("\"\n", log_get_stream() ); + fputs("\n", log_get_stream() ); count++; } if( !count ) { /* just in case that we have no valid textual userid */ + char *p; + /* Try for an invalid textual userid */ for( un=keyblock; un; un = un->next ) { if( un->pkt->pkttype == PKT_USER_ID && @@ -1414,29 +1698,37 @@ check_sig_and_print( CTX c, KBNODE node ) un? un->pkt->pkt.user_id->len:3, -1 ); - log_info(rc? _("BAD signature from \"") - : sig->flags.expired ? _("Expired signature from \"") - : _("Good signature from \"")); - if (opt.trust_model!=TM_ALWAYS && un) { - fputs(_("[uncertain]"), log_get_stream() ); + if(un) + p=utf8_to_native(un->pkt->pkt.user_id->name, + un->pkt->pkt.user_id->len,0); + else + p=xstrdup("[?]"); + + if(rc) + log_info(_("BAD signature from \"%s\""),p); + else if(sig->flags.expired) + log_info(_("Expired signature from \"%s\""),p); + else + log_info(_("Good signature from \"%s\""),p); + if (opt.trust_model!=TM_ALWAYS && un) + { putc(' ', log_get_stream() ); - } - print_utf8_string( log_get_stream(), - un? un->pkt->pkt.user_id->name:"[?]", - un? un->pkt->pkt.user_id->len:3 ); - fputs("\"\n", log_get_stream() ); + fputs(_("[uncertain]"), log_get_stream() ); + } + fputs("\n", log_get_stream() ); } /* If we have a good signature and already printed * the primary user ID, print all the other user IDs */ if ( count && !rc ) { + char *p; for( un=keyblock; un; un = un->next ) { if( un->pkt->pkttype != PKT_USER_ID ) continue; - if ( un->pkt->pkt.user_id->is_revoked ) - continue; - if ( un->pkt->pkt.user_id->is_expired ) - continue; + if((un->pkt->pkt.user_id->is_revoked + || un->pkt->pkt.user_id->is_expired) + && !(opt.verify_options&VERIFY_SHOW_UNUSABLE_UIDS)) + continue; /* Only skip textual primaries */ if ( un->pkt->pkt.user_id->is_primary && !un->pkt->pkt.user_id->attrib_data ) @@ -1451,41 +1743,53 @@ check_sig_and_print( CTX c, KBNODE node ) un->pkt->pkt.user_id->numattribs,pk,NULL); } - log_info( _(" aka \"")); - print_utf8_string( log_get_stream(), un->pkt->pkt.user_id->name, - un->pkt->pkt.user_id->len ); - if(opt.verify_options&VERIFY_SHOW_VALIDITY) - fprintf (log_get_stream(), "\" [%s]\n", - trust_value_to_string(get_validity(pk, - un->pkt-> - pkt.user_id))); + p=utf8_to_native(un->pkt->pkt.user_id->name, + un->pkt->pkt.user_id->len,0); + log_info(_(" aka \"%s\""),p); + xfree(p); + + if(opt.verify_options&VERIFY_SHOW_UID_VALIDITY) + { + const char *valid; + if(un->pkt->pkt.user_id->is_revoked) + valid=_("revoked"); + else if(un->pkt->pkt.user_id->is_expired) + valid=_("expired"); + else + valid=trust_value_to_string(get_validity(pk, + un->pkt-> + pkt.user_id)); + fprintf(log_get_stream()," [%s]\n",valid); + } else - fputs("\"\n", log_get_stream() ); + fputs("\n", log_get_stream() ); } } release_kbnode( keyblock ); if( !rc ) { - if(opt.verify_options&VERIFY_SHOW_POLICY) + if(opt.verify_options&VERIFY_SHOW_POLICY_URLS) show_policy_url(sig,0,1); else show_policy_url(sig,0,2); - if(opt.verify_options&VERIFY_SHOW_KEYSERVER) + if(opt.verify_options&VERIFY_SHOW_KEYSERVER_URLS) show_keyserver_url(sig,0,1); else show_keyserver_url(sig,0,2); - if(opt.verify_options&VERIFY_SHOW_NOTATION) - show_notation(sig,0,1); + if(opt.verify_options&VERIFY_SHOW_NOTATIONS) + show_notation(sig,0,1, + ((opt.verify_options&VERIFY_SHOW_STD_NOTATIONS)?1:0)+ + ((opt.verify_options&VERIFY_SHOW_USER_NOTATIONS)?2:0)); else - show_notation(sig,0,2); - } + show_notation(sig,0,2,0); + } if( !rc && is_status_enabled() ) { /* print a status response with the fingerprint */ - PKT_public_key *vpk = xcalloc (1, sizeof *vpk ); + PKT_public_key *vpk = xmalloc_clear( sizeof *vpk ); if( !get_pubkey( vpk, sig->keyid ) ) { byte array[MAX_FINGERPRINT_LEN], *p; @@ -1513,7 +1817,7 @@ check_sig_and_print( CTX c, KBNODE node ) akid[0] = vpk->main_keyid[0]; akid[1] = vpk->main_keyid[1]; free_public_key (vpk); - vpk = xcalloc (1, sizeof *vpk ); + vpk = xmalloc_clear( sizeof *vpk ); if (get_pubkey (vpk, akid)) { /* impossible error, we simply return a zeroed out fpr */ n = MAX_FINGERPRINT_LEN < 20? MAX_FINGERPRINT_LEN : 20; @@ -1530,14 +1834,18 @@ check_sig_and_print( CTX c, KBNODE node ) free_public_key( vpk ); } - if( !rc ) + if (!rc) + { + if(opt.verify_options&VERIFY_PKA_LOOKUPS) + pka_uri_from_sig (sig); /* Make sure PKA info is available. */ rc = check_signatures_trust( sig ); + } if(sig->flags.expired) { log_info(_("Signature expired %s\n"), asctimestamp(sig->expiredate)); - rc=GPG_ERR_GENERAL; /* need a better error here? */ + rc=G10ERR_GENERAL; /* need a better error here? */ } else if(sig->expiredate) log_info(_("Signature expires %s\n"),asctimestamp(sig->expiredate)); @@ -1553,19 +1861,19 @@ check_sig_and_print( CTX c, KBNODE node ) if( opt.batch && rc ) g10_exit(1); } - else { + else { char buf[50]; sprintf(buf, "%08lX%08lX %d %d %02x %lu %d", (ulong)sig->keyid[0], (ulong)sig->keyid[1], sig->pubkey_algo, sig->digest_algo, sig->sig_class, (ulong)sig->timestamp, rc ); write_status_text( STATUS_ERRSIG, buf ); - if( gpg_err_code (rc) == GPG_ERR_NO_PUBKEY ) { + if( rc == G10ERR_NO_PUBKEY ) { buf[16] = 0; write_status_text( STATUS_NO_PUBKEY, buf ); } - if( rc != GPG_ERR_NOT_PROCESSED ) - log_error(_("Can't check signature: %s\n"), gpg_strerror (rc) ); + if( rc != G10ERR_NOT_PROCESSED ) + log_error(_("Can't check signature: %s\n"), g10_errstr(rc) ); } return rc; } @@ -1595,7 +1903,6 @@ proc_tree( CTX c, KBNODE node ) if (!node) return; - c->local_id = 0; c->trustletter = ' '; if( node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) { @@ -1611,12 +1918,16 @@ proc_tree( CTX c, KBNODE node ) if( !c->have_data ) { free_md_filter_context( &c->mfx ); /* prepare to create all requested message digests */ - gcry_md_open (&c->mfx.md, 0, 0); - - /* fixme: why looking for the signature packet and not 1passpacket*/ - for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); ) { - gcry_md_enable ( c->mfx.md, n1->pkt->pkt.signature->digest_algo); - } + if (gcry_md_open (&c->mfx.md, 0, 0)) + BUG (); + + /* fixme: why looking for the signature packet and not the + one-pass packet? */ + for ( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); ) + { + gcry_md_enable (c->mfx.md, + n1->pkt->pkt.signature->digest_algo); + } /* ask for file and hash it */ if( c->sigs_only ) { rc = hash_datafiles( c->mfx.md, NULL, @@ -1629,7 +1940,7 @@ proc_tree( CTX c, KBNODE node ) n1? (n1->pkt->pkt.onepass_sig->sig_class == 0x01):0 ); } if( rc ) { - log_error("can't hash datafile: %s\n", gpg_strerror (rc)); + log_error("can't hash datafile: %s\n", g10_errstr(rc)); return; } } @@ -1690,20 +2001,24 @@ proc_tree( CTX c, KBNODE node ) else if( !c->have_data ) { /* detached signature */ free_md_filter_context( &c->mfx ); - gcry_md_open (&c->mfx.md, sig->digest_algo, 0); + if (gcry_md_open (&c->mfx.md, sig->digest_algo, 0)) + BUG (): + if( !opt.pgp2_workarounds ) ; else if( sig->digest_algo == DIGEST_ALGO_MD5 && is_RSA( sig->pubkey_algo ) ) { /* enable a workaround for a pgp2 bug */ - gcry_md_open (&c->mfx.md2, DIGEST_ALGO_MD5, 0 ); + if (gcry_md_open (&c->mfx.md2, DIGEST_ALGO_MD5, 0)) + BUG (): } else if( sig->digest_algo == DIGEST_ALGO_SHA1 && sig->pubkey_algo == PUBKEY_ALGO_DSA && sig->sig_class == 0x01 ) { /* enable the workaround also for pgp5 when the detached * signature has been created in textmode */ - gcry_md_open (&c->mfx.md2, sig->digest_algo, 0 ); + if (gcry_md_open (&c->mfx.md2, sig->digest_algo, 0 )) + BUG (); } #if 0 /* workaround disabled */ /* Here we have another hack to work around a pgp 2 bug @@ -1716,9 +2031,9 @@ proc_tree( CTX c, KBNODE node ) /* c->mfx.md2? 0 :(sig->sig_class == 0x01) */ #endif if ( DBG_HASHING ) { - gcry_md_start_debug ( c->mfx.md, "verify" ); + gcry_md_start_debug( c->mfx.md, "verify" ); if ( c->mfx.md2 ) - gcry_md_start_debug ( c->mfx.md2, "verify2" ); + gcry_md_start_debug( c->mfx.md2, "verify2" ); } if( c->sigs_only ) { rc = hash_datafiles( c->mfx.md, c->mfx.md2, @@ -1731,7 +2046,7 @@ proc_tree( CTX c, KBNODE node ) (sig->sig_class == 0x01) ); } if( rc ) { - log_error("can't hash datafile: %s\n", gpg_strerror (rc)); + log_error("can't hash datafile: %s\n", g10_errstr(rc)); return; } } @@ -1739,8 +2054,6 @@ proc_tree( CTX c, KBNODE node ) log_error (_("not a detached signature\n") ); return; } - else if ( c->pipemode.op == 'B' ) - ; /* this is a detached signature trough the pipemode handler */ else if (!opt.quiet) log_info(_("old style (PGP 2.x) signature\n")); |