OpenCA Security Advisory [28 November 2003] Vulnerabilities in signature validation ======================================= Multiple flaws in OpenCA before version 0.9.1.4 could cause OpenCA to use an incorrect certificate in the chain to determine the serial being checked which could lead to certificates that are revoked or expired being incorrectly accepted. Chris Covell and Gottfried Scheckenbach performed tests with OpenCA and CA hierarchies. They had problems to verify signatures with some functions in OpenCA which test the signer's certificate. Michael Bell of the OpenCA core team identified and fixed the problems for OpenCA 0.9.1 and the CVS HEAD. Vulnerabilities ----------------- 1. OpenCA has a library for common crypto operations - crypto-utils.lib. This library includes a function to determine the serial of the certificate which somebody used to create a PKCS#7 signature. The function uses this serial to load and return the certificate. The function used the interface of OpenCA::PKCS7 (the OpenCA PKCS#7 module) in a wrong way. 2. The crypto library crypto-utils.lib uses all certificates which were included into the signature to create the X.509 object of the signer's certificate. The result is a object which was created from one of the certificates of the certificate chain. This means that the result is haphazard. 3. OpenCA::PKCS7 includes a wrong regular expression to detect lines which have nothing to do with the parsing of the certificate chain. 4. The serial in the certificate chain were parsed with a wrong regular expression in OpenCA::PKCS7. Big letters like A, C, B, D, E and F were ignored. Who is affected? ------------------ All version of OpenCA including 0.9.1.3. A security risk is present for people who are using digital signatures to secure approved requests or role based access control (RBAC). Recommendations ----------------- Upgrade to 0.9.1.4 and use newer snapshots than openca-0.9-SNAP-20031125.tar.gz. You can fix the problem by yourself too with the included patches. The original files which we used to create the diffs are from OpenCA 0.9.1.3. -----BEGIN PATCH----- --- openca-0.9.1.3/src/modules/openca-pkcs7/PKCS7.pm 2002-09-10 16:42:02.000000000 +0200 +++ openca-0.9.1.4/src/modules/openca-pkcs7/PKCS7.pm 2003-11-26 15:54:08.000000000 +0100 @@ -69,7 +69,7 @@ our ($errno, $errval); -($OpenCA::PKCS7::VERSION = '$Revision: 1.12 $' )=~ s/(?:^.*: (\d+))|(?:\s+\$$)/defined $1?"0\.9":""/eg; +($OpenCA::PKCS7::VERSION = '$Revision: 1.12.2.1 $' )=~ s/(?:^.*: (\d+))|(?:\s+\$$)/defined $1?"0\.9":""/eg; my %params = ( inFile => undef, @@ -167,6 +167,8 @@ my ( $ret, $tmp ); + return $self->{parsed} if ($self->{parsed}); + $tmp = $self->{backend}->verify( SIGNATURE=>$self->{signature}, DATA_FILE=>$self->{dataFile}, CA_CERT=>$self->{caCert}, @@ -292,10 +294,10 @@ ($self->{status}) = ( $line =~ /^\s*error:([^:]*):/ ); } - next if( $line != /^depth/i ); + next if( $line !~ /^depth/i ); ( $currentDepth, $serial, $dn ) = - ( $line =~ /depth:([\d]+) serial:([a-f\d]+) subject:(.*)/ ); + ( $line =~ /depth:([\d]+) serial:([a-fA-F\d]+) subject:(.*)/ ); $ret->{$currentDepth}->{SERIAL} = hex ($serial) ; $ret->{$currentDepth}->{DN} = $dn; --- openca-0.9.1.3/src/common/lib/functions/crypto-utils.lib 2002-12-22 13:08:19.000000000 +0100 +++ openca-0.9.1.4/src/common/lib/functions/crypto-utils.lib 2003-11-26 13:04:50.000000000 +0100 @@ -176,19 +176,36 @@ return undef; } - ## Get signer certificate from the pkcs7 structure - $sigCert = new OpenCA::X509 ( SHELL => $cryptoShell, - DATA => $sig->getSigner()->{CERTIFICATE}); - - if( not $sigCert ) { - $errno = 6103; - $errval = i18nGettext ("Signer's certificate is corrupt!\nOpenCA::X509 returns errorcode __ERRNO__ (__ERRVAL__).", - "__ERRNO__", $OpenCA::X509::errno, - "__ERRVAL__", $OpenCA::X509::errval); - return undef; + ## Get signer certificate chain from the pkcs7 structure + my @chain = split /-----END CERTIFICATE-----/, + $sig->getSigner()->{CERTIFICATE}; + for (my $i=0; $i < scalar @chain; $i++) + { + if (not $chain[$i]) + { + delete $chain[$i]; + next; + } + $chain[$i] .= "-----END CERTIFICATE-----"; + $chain[$i] =~ s/^.*-----BEGIN CERTIFICATE-----/-----BEGIN CERTIFICATE-----/s; + } + $sigCert = undef; + for (my $i=0; $i < scalar @chain; $i++) + { + $sigCert = new OpenCA::X509 ( SHELL => $cryptoShell, + DATA => $chain[$i]); + if( not $sigCert ) { + $errno = 6103; + $errval = i18nGettext ("Signer's certificate is corrupt!\nOpenCA::X509 returns errorcode __ERRNO__ (__ERRVAL__).", + "__ERRNO__", $OpenCA::X509::errno, + "__ERRVAL__", $OpenCA::X509::errval); + return undef; + } + last if ( $tmpCert->getSerial() eq $sigCert->getSerial() ); + $sigCert = undef; } - if( $tmpCert->getSerial() ne $sigCert->getSerial() ) { + if( not $sigCert ) { $errno = 6104; $errval = gettext ("Signer's Certificate and DB's Certificate do not match"); return undef; @@ -281,19 +298,8 @@ return undef; } - my $sigCert = new OpenCA::X509 ( SHELL => $cryptoShell, - DATA => $sig->getSigner()->{CERTIFICATE}); - - if (not $sigCert) { - $errno = 6302; - $errval = i18nGettext ("Cannot create X509-object from the certificate of the signer! OpenCA::X509 returns errorcode __ERRNO__ (__ERRVAL__).", - "__ERRNO__", $OpenCA::X509::errno, - "__ERRVAL__", $OpenCA::X509::errval); - return undef; - } - my $db_cert = $db->getItem( DATATYPE => 'CERTIFICATE', - KEY => $sigCert->getSerial() ); + KEY => $sig->getSigner()->{SERIAL} ); if( not $db_cert ) { $errno = 6303; --- openca-0.9.1.3/src/common/lib/cmds/verifySignature 2003-03-31 15:45:19.000000000 +0200 +++ openca-0.9.1.4/src/common/lib/cmds/verifySignature 2003-11-26 13:04:34.000000000 +0100 @@ -11,7 +11,7 @@ ## Get the Configuration parameters ... my ( $parsed, $lnk, $serLink, $sigInfo, $sigStatus, $signer, $signature); my ( $baseDoc, $info, $sigCertStatus, $def, $dbStatus, $dbMessage); -my ( $myCN, $myEmail, $mySerial, @sigCert, $tmpCert, $pCert ); +my ( $myCN, $myEmail, $mySerial, $tmpCert, $pCert ); ## Get Required Parameters from Configuration my $baseDoc = getRequired ('verifySignatureform'); @@ -53,10 +53,7 @@ $myDN = $signer->{DN}; $myDN =~ s/^\///; $myDN =~ s/\//
/g; -$sigCert = new OpenCA::X509 ( SHELL => $cryptoShell, - DATA => $sign->getSigner()->{CERTIFICATE}); - -$issuerDN = $sigCert->getParsed()->{ISSUER}; +$issuerDN = $sign->getParsed()->{CHAIN}->{1}->{DN}; $issuerDN =~ s/^\///; $issuerDN =~ s/[\/\,]/
/g; ## Check Signature Status @@ -71,7 +68,7 @@ $dbStatus = $errno; $sigStatus = "".gettext("Unknown").""; - $serLink = $sigCert->getSerial(); + $serLink = $sign->getSigner()->{SERIAL}; } else { $sigMessage = gettext("Signature correctly verified"); } @@ -96,11 +93,7 @@ $serLink = $tmpCert->getSerial(); } -if( $sigCert ) { - $pCert = $sigCert->getParsed(); -} elsif ( $tmpCert ) { - $pCert = $tmpCert->getParsed(); -} +$pCert = $tmpCert->getParsed(); ## View the Operator Used Certificate Data $page = $query->subVar( $page, '@DN@', ($myDN or "n/a" ) ); --- openca-0.9.1.3/src/common/lib/cmds/viewSignature 2002-12-10 16:18:15.000000000 +0100 +++ openca-0.9.1.4/src/common/lib/cmds/viewSignature 2003-11-26 13:04:34.000000000 +0100 @@ -11,7 +11,7 @@ ## Get the Configuration parameters ... my ( $parsed, $lnk, $serLink, $sigInfo, $sigStatus, $signer, $signature); my ( $baseDoc, $info, $sigCertStatus, $def, $dbStatus, $dbMessage); -my ( $myCN, $myEmail, $mySerial, @sigCert, $tmpCert, $pCert ); +my ( $myCN, $myEmail, $mySerial, $tmpCert, $pCert ); my $dataType = $query->param('dataType' ); my $key = $query->param('key'); @@ -54,9 +54,6 @@ name=>"EMAIL", value=>$signer->{DN_HASH}->{EMAILADDRESS}[0]} ); $myEmail = $lnk->a({-href=>$lnk->self_url()}, $signer->{DN_HASH}->{EMAILADDRESS}[0]); -$sigCert = new OpenCA::X509 ( SHELL => $cryptoShell, - DATA => $signature->getSigner()->{CERTIFICATE}); - ## Check Signature Status if( not libCheckSignature( SIGNATURE=>$signature ) ) { $sigStatus = "".gettext("Error").""; @@ -105,7 +102,7 @@ $serLink = $lnk->a({-href=>$lnk->self_url()}, $tmpCert->getSerial() ); - $decSerLink = "( " . hex( $sigCert->getSerial() ) . " )"; + $decSerLink = "( " . hex( $tmpCert->getSerial() ) . " )"; $lnk = new CGI({cmd => "search", dataType => "CERTIFICATE", @@ -114,11 +111,7 @@ $myEmail = $lnk->a({-href=>$lnk->self_url()}, $tmpCert->getParsed()->{EMAILADDRESS}); } -if( $sigCert ) { - $pCert = $sigCert->getParsed(); -} elsif ( $tmpCert ) { - $pCert = $tmpCert->getParsed(); -} +$pCert = $tmpCert->getParsed(); ## View the Operator Used Certificate Data $page = $query->subVar( $page, '@CN@', ($myCN or "n/a" ) ); -----END PATCH----- References ------------ The Common Vulnerabilities and Exposures project (cve.mitre.org) has assigned the name CAN-2003-0960 to this issue. http://cve.mitre.org/cgi-bin/cvename.cgi?name=CAN-2003-0960 URL for this Security Advisory: http://www.openca.org/news/CAN-2003-0960.txt