1
0
Fork 0
mirror of git://git.gnupg.org/gnupg.git synced 2025-07-03 22:56:33 +02:00

Stricter test of allowed signature packet compositions.

There is still one problem to solve.
This commit is contained in:
Werner Koch 2006-03-06 21:28:25 +00:00
parent a200f76dcf
commit 3e08d87168
7 changed files with 304 additions and 128 deletions

View file

@ -1,7 +1,8 @@
2006-03-06 Werner Koch <wk@g10code.com>
* mainproc.c (check_sig_and_print): Check for multiple plaintexts
before a signature. Reported by Tavis Ormandy.
* mainproc.c (check_sig_and_print): Made the composition test more
tight. This is due to another bug report by Tavis Ormandy.
(add_onepass_sig): Simplified.
2006-03-05 Werner Koch <wk@g10code.com>

View file

@ -114,27 +114,14 @@ release_list( CTX c )
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;
}
@ -1416,93 +1403,118 @@ pka_uri_from_sig (PKT_signature *sig)
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;
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;
}
/* 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.
*/
if (opt.skip_verify)
{
KBNODE n;
int n_sig = 0;
int n_plaintext = 0;
int sig_seen, onepass_seen;
for (n=c->list; n; n=n->next )
{
if ( n->pkt->pkttype == PKT_SIGNATURE )
n_sig++;
else if (n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK) )
n_plaintext++;
}
for (sig_seen=onepass_seen=0,n=c->list; n; n=n->next )
{
if (n->pkt->pkttype == PKT_ONEPASS_SIG)
{
onepass_seen++;
}
else if (n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_CLEARSIGN_START) )
{
onepass_seen++; /* Handle the same way as a onepass. */
}
else if ( (sig_seen && n->pkt->pkttype != PKT_SIGNATURE) )
{
log_error(_("can't handle these multiple signatures\n"));
return 0;
}
else if ( n->pkt->pkttype == PKT_SIGNATURE )
{
sig_seen = 1;
}
else if (n_sig > 1 && !sig_seen && !onepass_seen
&& n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK) )
{
/* Plaintext before signatures but no onepass
signature packets. */
log_error(_("can't handle these multiple signatures\n"));
return 0;
}
else if (n_plaintext > 1 && !sig_seen && !onepass_seen
&& n->pkt->pkttype == PKT_GPG_CONTROL
&& (n->pkt->pkt.gpg_control->control
== CTRLPKT_PLAINTEXT_MARK) )
{
/* More than one plaintext before a signature but no
onepass packets. */
log_error(_("can't handle this ambiguous signed data\n"));
return 0;
}
}
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 || !n_sig)
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.) */
astr = pubkey_algo_to_string( sig->pubkey_algo );
if(keystrlen()>8)
{
@ -1926,7 +1938,8 @@ proc_tree( CTX c, KBNODE node )
/* prepare to create all requested message digests */
c->mfx.md = md_open(0, 0);
/* fixme: why looking for the signature packet and not 1passpacket*/
/* fixme: why looking for the signature packet and not the
one-pass packet? */
for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); ) {
md_enable( c->mfx.md, n1->pkt->pkt.signature->digest_algo);
}