1## OpenCA::PKCS7 2## 3## Copyright (C) 1998-1999 Massimiliano Pala (madwolf@openca.org) 4## All rights reserved. 5## 6## This library is free for commercial and non-commercial use as long as 7## the following conditions are aheared to. The following conditions 8## apply to all code found in this distribution, be it the RC4, RSA, 9## lhash, DES, etc., code; not just the SSL code. The documentation 10## included with this distribution is covered by the same copyright terms 11## 12## // Copyright remains Massimiliano Pala's, and as such any Copyright notices 13## in the code are not to be removed. 14## If this package is used in a product, Massimiliano Pala should be given 15## attribution as the author of the parts of the library used. 16## This can be in the form of a textual message at program startup or 17## in documentation (online or textual) provided with the package. 18## 19## Redistribution and use in source and binary forms, with or without 20## modification, are permitted provided that the following conditions 21## are met: 22## 1. Redistributions of source code must retain the copyright 23## notice, this list of conditions and the following disclaimer. 24## 2. Redistributions in binary form must reproduce the above copyright 25## notice, this list of conditions and the following disclaimer in the 26## documentation and/or other materials provided with the distribution. 27## 3. All advertising materials mentioning features or use of this software 28## must display the following acknowledgement: 29## // "This product includes OpenCA software written by Massimiliano Pala 30## // (madwolf@openca.org) and the OpenCA Group (www.openca.org)" 31## 4. If you include any Windows specific code (or a derivative thereof) from 32## some directory (application code) you must include an acknowledgement: 33## "This product includes OpenCA software (www.openca.org)" 34## 35## THIS SOFTWARE IS PROVIDED BY OPENCA DEVELOPERS ``AS IS'' AND 36## ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 37## IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 38## ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 39## FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 40## DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 41## OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 42## HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 43## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 44## OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 45## SUCH DAMAGE. 46## 47## The licence and distribution terms for any publically available version or 48## derivative of this code cannot be changed. i.e. this code cannot simply be 49## copied and put under another distribution licence 50## [including the GNU Public Licence.] 51## 52 53## // the module's errorcode is 79 54## 55## functions 56## 57## new 11 58## initSignature 12 59## getParsed 21 60## getSigner 22 61## verifyChain 31 62## parseDepth 32 63## getSignature 23 64 65use strict; 66use Carp qw(cluck); 67use X500::DN; 68 69package OpenCA::PKCS7; 70 71our ($errno, $errval); 72 73($OpenCA::PKCS7::VERSION = '$Revision: 1.1.1.1 $' )=~ s/(?:^.*: (\d+))|(?:\s+\$$)/defined $1?"0\.9":""/eg; 74 75my %params = ( 76 inFile => undef, 77 signature => undef, 78 dataFile => undef, 79 caCert => undef, 80 caDir => undef, 81 parsed => undef, 82 context => undef, 83 backend => undef, 84 status => undef, 85 statusStack => [], 86 nochain => undef, 87 opaquesig => undef, 88 ignoreErrors => undef, 89 ignoreErrorsHash => undef, 90); 91 92sub setError { 93 my $self = shift; 94 95 if (scalar (@_) == 4) { 96 my $keys = { @_ }; 97 $self->{errno} = $keys->{ERRNO}; 98 $self->{errval} = $keys->{ERRVAL}; 99 } else { 100 $self->{errno} = $_[0]; 101 $self->{errval} = $_[1]; 102 } 103 $errno = $self->{errno}; 104 $errval = $self->{errval}; 105 106 ## support for: return $self->setError (1234, "Something fails.") if (not $xyz); 107 return undef; 108} 109 110sub errno 111{ 112 my $self = shift; 113 return $self->{errno}; 114} 115 116sub errval 117{ 118 my $self = shift; 119 return $self->{errval}; 120} 121 122## Create an instance of the Class 123sub new { 124 my $that = shift; 125 my $class = ref($that) || $that; 126 127 my $self = { 128 %params, 129 }; 130 131 bless $self, $class; 132 133 my $keys = { @_ }; 134 my $tmp; 135 136 $self->{gettext} = $keys->{GETTEXT}; 137 return $self->setError (7911020, "OpenCA::PKCS7->new: The translation function must be specified.") 138 if (not $self->{gettext}); 139 140 $self->{caCert} = $keys->{CA_CERT}; 141 $self->{caDir} = $keys->{CA_DIR}; 142 143 $self->{dataFile} = $keys->{DATAFILE}; 144 $self->{data} = $keys->{DATA}; 145 146 $self->{inFile} = $keys->{INFILE}; 147 $self->{signature} = $keys->{SIGNATURE}; 148 149 $self->{backend} = $keys->{SHELL}; 150 151 $self->{nochain} = $keys->{NOCHAIN}; 152 $self->{opaquesig} = $keys->{OPAQUESIGNATURE}; 153 154 $self->{ignoreErrors} = $keys->{IGNOREERRORS}; 155 156 157 # you can explicitly exclude certain error codes from being 158 # returned. to use this features, call the constructor with the 159 # error(s) to exclude: 160 # 161 # my $sig = new OpenCA::PKCS7(..., IGNOREERRORS => 26, ...) 162 # 163 # or to exclude more than one error: 164 # my $sig = new OpenCA::PKCS7(..., IGNOREERRORS => [26, 18], ...) 165 # 166 167 # convert to arrayref if a scalar was passed in 168 if (defined $self->{ignoreErrors} and not ref($self->{ignoreErrors})) { 169 $self->{ignoreErrors} = [ $self->{ignoreErrors} ]; 170 } 171 $self->{ignoreErrors} = [] unless defined $self->{ignoreErrors}; 172 173 # nochain: ignore errors on selfsigned certs and incorrect key usage 174 push(@{$self->{ignoreErrors}}, (18, 26)) if (exists $self->{nochain} and $self->{nochain}); 175 176 # create hash from the list of errors to ignore 177 $self->{ignoreErrorsHash} = { map { $_, 1 } @{$self->{ignoreErrors}}}; 178 179 180 if( ($self->{inFile}) and ( -e "$self->{inFile}") ) { 181 $self->{signature} = ""; 182 183 open(FD, "<$self->{inFile}" ) 184 or return $self->setError (7911021, 185 $self->{gettext} ("OpenCA::PKCS7->new: Cannot open infile __FILENAME__ for reading.", 186 "__FILENAME__", $self->{inFile})); 187 while ( $tmp = <FD> ) { 188 $self->{signature} .= $tmp; 189 } 190 close(FD); 191 }; 192 193 if( ($self->{dataFile}) and ( -e "$self->{dataFile}") ) { 194 $self->{data} = ""; 195 196 open(FD, "<$self->{dataFile}" ) 197 or return $self->setError (7911023, 198 $self->{gettext} ("OpenCA::PKCS7->new: Cannot open datafile __FILENAME__ for reading.", 199 "__FILENAME__", $self->{dataFile})); 200 while ( $tmp = <FD> ) { 201 $self->{data} .= $tmp; 202 } 203 close(FD); 204 }; 205 206 if (not $self->{opaquesig} and not $self->{data} and $self->{inFile} and ( -e "$self->{inFile}")) { 207 $self->{data} = ""; 208 209 open(FD, "<$self->{inFile}" ) 210 or return $self->setError (7911025, 211 $self->{gettext} ("OpenCA::PKCS7->new: Cannot open infile __FILENAME__ for reading.", 212 "__FILENAME__", $self->{inFile})); 213 while ( $tmp = <FD> ) { 214 $self->{data} .= $tmp; 215 } 216 close(FD); 217 }; 218 219 return $self->setError (7911031, 220 $self->{gettext} ("OpenCA::PKCS7->new: Cannot initialize signature (__ERRNO__). __ERRVAL__", 221 "__ERRNO__", $self->errno, 222 "__ERRVAL__", $self->errval)) 223 if (not $self->initSignature() ); 224 225 return $self; 226} 227 228sub initSignature { 229 my $self = shift; 230 my $keys = { @_ }; 231 my $tmp; 232 233 return $self->setError (7912011, 234 $self->{gettext} ("OpenCA::PKCS7->initSignature: No signature specified.")) 235 if (not $self->{signature}); 236 return $self->setError (7912012, 237 $self->{gettext} ("OpenCA::PKCS7->initSignature: No data specified.")) 238 if (not $self->{data} and (not $self->{opaquesig})); 239 240 if( $self->getParsed() ) { 241 return 1; 242 } else { 243 return $self->setError (7912021, 244 $self->{gettext} ("OpenCA::PKCS7->initSignature: Cannot parse signature (__ERRNO__). __ERRVAL__", 245 "__ERRNO__", $self->errno, 246 "__ERRVAL__", $self->errval)) 247 } 248} 249 250sub getParsed { 251 my $self = shift; 252 my $keys = { @_ }; 253 254 my ( $ret, $tmp ); 255 256 $tmp = $self->{backend}->verify( SIGNATURE=>$self->{signature}, 257 OPAQUESIGNATURE => $self->{opaquesig}, 258 ## old: DATA_FILE=>$self->{dataFile}, 259 DATA => $self->{data}, 260 CA_CERT => $self->{caCert}, 261 CA_DIR => $self->{caDir}, 262 VERBOSE => 1); 263 print "OpenCA::PKCS7->getParsed: OpenCA::OpenSSL->verify returns:<br>\n". 264 $tmp."<br>\n" 265 if ($self->{DEBUG}); 266 267 ## why should verify the signature twice? 268 #$tmp = $self->{backend}->verify( SIGNATURE=>$self->{signature}, 269 # OPAQUESIGNATURE => $self->{opaquesig}, 270 # DATA_FILE=>$self->{dataFile}, 271 # NOCHAIN=>1, 272 # VERBOSE=>1 ); 273 274 if (not $tmp) { 275 if (not $self->{ignoreErrorsHash}->{$OpenCA::OpenSSL::errno}) { 276 $self->setstatus($OpenCA::OpenSSL::errno); 277 278 return $self->setError (7921021, 279 $self->{gettext} ("OpenCA::PKCS7->getParsed: The crypto-backend cannot verify the signature (__ERRNO__). __ERRVAL__", 280 "__ERRNO__", $OpenCA::OpenSSL::errno, 281 "__ERRVAL__", $OpenCA::OpenSSL::errval)); 282 } 283 } 284 285 286 if ( not $ret = $self->parseDepth( DEPTH=>"0", DATA=>$tmp ) ) { 287 print STDERR "Error on output of openca-sv.\n$tmp\n"; 288 if (not $self->{ignoreErrorsHash}->{$OpenCA::OpenSSL::errno}) { 289 $self->setstatus($OpenCA::OpenSSL::errno); 290 291 return $self->setError (7921031, 292 $self->{gettext} ("OpenCA::PKCS7->getParsed: Cannot parse the signer (__ERRNO__). __ERRVAL__", 293 "__ERRNO__", $self->errno, 294 "__ERRVAL__", $self->errval)); 295 } 296 } 297 298 $self->{parsed}->{SIGNER} = $ret->{0}; 299 300 if ( ( $tmp ) and ( $ret = $self->parseDepth( DATA=>$tmp )) ) { 301 $self->{parsed}->{CHAIN} = $ret; 302 } 303 304 $self->{parsed}->{SIGNER}->{CERTIFICATE} = 305 $self->{backend}->pkcs7Certs( PKCS7=>$self->{signature}); 306 if (not defined $self->{parsed}->{SIGNER}->{CERTIFICATE}) 307 { 308 return $self->setError (7921033, 309 $self->{gettext} ("OpenCA::PKCS7->getParsed: The extraction of the certificates from the signature failed (__ERRNO__). __ERRVAL__", 310 "__ERRNO__", $self->{backend}->errno, 311 "__ERRVAL__", $self->{backend}->errval)); 312 } 313 314 $self->{parsed}->{SIGNATURE} = $self->{signature}; 315 316 return $self->{parsed}; 317} 318 319# returns the last status set by the module 320# Options: 321# FORMAT => ARRAY: 322# return arrayref containing an array of all error codes encountered, 323# individual error codes may appear more than once 324# FORMAT => HASH: 325# return hashref containing all error codes encountered (key: errorcode, 326# value: 1) 327# FORMAT not set or other value: return last error 328sub status { 329 my $self = shift; 330 my $keys = { @_ }; 331 332 if (exists $keys->{FORMAT} and $keys->{FORMAT} eq "ARRAY") { 333 return $self->{statusStack}; 334 } elsif (exists $keys->{FORMAT} and $keys->{FORMAT} eq "HASH") { 335 return { map { $_, 1 } @{$self->{statusStack}}}; 336 } 337 338 return $self->{status}; 339} 340 341 342# add one single error to the error stack 343sub setstatus { 344 my $self = shift; 345 my $err = shift; 346 347 $self->{status} = $err; 348 if (ref($self->{statusStack}) eq "ARRAY") { 349 push(@{$self->{statusStack}}, $err); 350 } else { 351 carp("self->{statusStack} is not an Arrayref"); 352 } 353} 354 355 356sub getSigner { 357 my $self = shift; 358 359 my $keys = { @_ }; 360 my ( $tmp, $ret ); 361 362 return $self->setError (7922011, 363 $self->{gettext} ("OpenCA::PKCS7->getSigner: The signature was not parsed.")) 364 if( not $self->{parsed} ); 365 return $self->{parsed}->{SIGNER}; 366} 367 368sub verifyChain { 369 my $self = shift; 370 371 my $keys = { @_ }; 372 my ( $tmp, $ret ); 373 374 #unnecessary because the signature was already loaded 375 #if ( $self->{inFile} ) { 376 # $tmp=$self->{backend}->verify( SIGNATURE_FILE => $self->{inFile}, 377 # OPAQUESIGNATURE => $self->{opaquesig}, 378 # DATA => $self->{data}, 379 # CA_CERT => $self->{caCert}, 380 # CA_DIR => $self->{caDir}, 381 # VERBOSE => 1 ); 382 #} else { 383 $tmp=$self->{backend}->verify( SIGNATURE => $self->{signature}, 384 OPAQUESIGNATURE => $self->{opaquesig}, 385 DATA => $self->{data}, 386 CA_CERT => $self->{caCert}, 387 CA_DIR => $self->{caDir}, 388 VERBOSE => 1 ); 389 #}; 390 391 return $self->setError (7931021, 392 $self->{gettext} ("OpenCA::PKCS7->verifyChain: The crypto-backend fails (__ERRNO__). __ERRVAL__", 393 "__ERRNO__", $OpenCA::OpenSSL::errno, 394 "__ERRVAL__", $OpenCA::OpenSSL::errval)) 395 if (not $tmp); 396 397 ## Returns if signature is not valid (verify returned an error) 398 return $self->setError (7931022, 399 $self->{gettext} ("OpenCA::PKCS7->verifyChain: The crypto-backend fails.")) 400 if( $? != 0 ); 401 402 if ( not $ret = $self->parseDepth( DEPTH=>"0", DATA=>$tmp ) ) { 403 print STDERR "Error on output of openca-sv.\n$tmp\n"; 404 return $self->setError (7931031, 405 $self->{gettext} ("OpenCA::PKCS7->verifyChain: Cannot parse the signer (__ERRNO__). __ERRVAL__", 406 "__ERRNO__", $self->errno, 407 "__ERRVAL__", $self->errval)); 408 } 409 410 return $ret; 411} 412 413sub parseDepth { 414 415 my $self = shift; 416 my $keys = { @_ }; 417 418 my $depth = $keys->{DEPTH}; 419 my $data = $keys->{DATA}; 420 my @dnList = (); 421 my @ouList = (); 422 423 my ( $serial, $dn, $email, $cn, @ou, $o, $c ); 424 my ( $currentDepth, $subject, $tmp, $line, $ret ); 425 426 return $self->setError (7932011, 427 $self->{gettext} ("OpenCA::PKCS7->parseDepth: No data specified.")) 428 if (not $data); 429 430 my @lines = split ( /(\n|\r)/ , $data ); 431 432 while( $line = shift @lines ) { 433 434 print "OpenCA::PKCS7->parseDepth: line: $line<br>\n" 435 if ($self->{DEBUG}); 436 if ($line =~ /^\s*error:20:/i and 437 not $self->{ignoreErrorsHash}->{20}) { 438 $self->setError (7932021, 439 $self->{gettext} ("OpenCA::PKCS7->parseDepth: The chain is not complete.")); 440 $self->setstatus(20); 441 } elsif ($line =~ /^\s*error:18:/i and 442 not $self->{ignoreErrorsHash}->{18}) { 443 $self->setError (7932023, 444 $self->{gettext} ("OpenCA::PKCS7->parseDepth: Selfsigned certificate at depth 0.")); 445 $self->setstatus(18); 446 } elsif ($line =~ /^\s*error:26:/i and 447 not $self->{ignoreErrorsHash}->{26}) { 448 ## OpenCA uses CA certs to sign the role 449 $self->setError (7932025, 450 $self->{gettext} ("OpenCA::PKCS7->parseDepth: One of the certificates was used for a not allowed operation. Usually a CA certificate signed data or a normal certificate acts as a CA. (__ERRVAL__)", 451 "__ERRVAL__", $line)); 452 $self->setstatus(26); 453 } elsif ($line =~ /^\s*error:([^:]*):/i and 454 not $self->{ignoreErrorsHash}->{$1}) { 455 $self->setError (7932039, 456 $self->{gettext} ("OpenCA::PKCS7->parseDepth: There is a problem with the verification of the chain. (__ERRVAL__)", 457 "__ERRVAL__", $line)); 458 $self->setstatus($1); 459 } 460 461 next if( $line !~ /^depth/i ); 462 463 ( $currentDepth, $serial, $dn ) = 464 ( $line =~ /depth:([\d]+) serial:([a-fA-F\d]+) subject:(.*)/ ); 465 use Math::BigInt; 466 my $serial_obj = Math::BigInt->new ('0x'.$serial); 467 $ret->{$currentDepth}->{SERIAL} = $serial_obj->bstr(); 468 $ret->{$currentDepth}->{DN} = $dn; 469 if ($self->{DEBUG}) 470 { 471 print "OpenCA::PKCS7->parseDepth: depth: $currentDepth<br>\n"; 472 print "OpenCA::PKCS7->parseDepth: serial:". 473 $ret->{$currentDepth}->{SERIAL}."<br>\n"; 474 print "OpenCA::PKCS7->parseDepth: dn:". 475 $ret->{$currentDepth}->{DN}."<br>\n"; 476 } 477 478 ## Split the Subject into separate fields 479 @dnList = split( /[\,\/]+/, $dn ); 480 @ouList = (); 481 482 my $tmpOU; 483 484 ## load the differnt parts of the DN into DN_HASH 485 print "OpenCA::PKCS7->parseDepth: DN: ".$dn."<br>\n" if ($self->{DEBUG}); 486 my $x500_dn = X500::DN->ParseRFC2253 ($dn); 487 if (not $x500_dn) { 488 print "OpenCA::PKCS7->parseDepth: X500::DN failed<br>\n" if ($self->{DEBUG}); 489 return $self->setError (7932081, 490 $self->{gettext} ("OpenCA::PKCS7->parseDepth: X500::DN failed.")); 491 return undef; 492 } 493 my $rdn; 494 foreach $rdn ($x500_dn->getRDNs()) { 495 next if ($rdn->isMultivalued()); 496 my @attr_types = $rdn->getAttributeTypes(); 497 my $type = $attr_types[0]; 498 my $value = $rdn->getAttributeValue ($type); 499 push (@{$ret->{$currentDepth}->{DN_HASH}->{uc($type)}}, $value); 500 print "OpenCA::PKCS7->parseDepth: DN_HASH: $type=$value<br>\n" if ($self->{DEBUG}); 501 } 502 503 } 504 505 return $ret; 506} 507 508sub getSignature { 509 my $self = shift; 510 511 return $self->setError (7923011, 512 $self->{gettext} ("OpenCA::PKCS7->getSignature: There is no signature present.")) 513 if( not $self->{signature} ); 514 return $self->{signature}; 515} 516 517sub getSignerCert { 518 my $self = shift; 519 520 my $keys = { @_ }; 521 522 523} 524 525# Autoload methods go after =cut, and are processed by the autosplit program. 526 5271; 528