Annotation of embedaddon/curl/lib/mk-ca-bundle.pl, revision 1.1

1.1     ! misho       1: #!/usr/bin/env perl
        !             2: # ***************************************************************************
        !             3: # *                                  _   _ ____  _
        !             4: # *  Project                     ___| | | |  _ \| |
        !             5: # *                             / __| | | | |_) | |
        !             6: # *                            | (__| |_| |  _ <| |___
        !             7: # *                             \___|\___/|_| \_\_____|
        !             8: # *
        !             9: # * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al.
        !            10: # *
        !            11: # * This software is licensed as described in the file COPYING, which
        !            12: # * you should have received as part of this distribution. The terms
        !            13: # * are also available at https://curl.haxx.se/docs/copyright.html.
        !            14: # *
        !            15: # * You may opt to use, copy, modify, merge, publish, distribute and/or sell
        !            16: # * copies of the Software, and permit persons to whom the Software is
        !            17: # * furnished to do so, under the terms of the COPYING file.
        !            18: # *
        !            19: # * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
        !            20: # * KIND, either express or implied.
        !            21: # *
        !            22: # ***************************************************************************
        !            23: # This Perl script creates a fresh ca-bundle.crt file for use with libcurl.
        !            24: # It downloads certdata.txt from Mozilla's source tree (see URL below),
        !            25: # then parses certdata.txt and extracts CA Root Certificates into PEM format.
        !            26: # These are then processed with the OpenSSL commandline tool to produce the
        !            27: # final ca-bundle.crt file.
        !            28: # The script is based on the parse-certs script written by Roland Krikava.
        !            29: # This Perl script works on almost any platform since its only external
        !            30: # dependency is the OpenSSL commandline tool for optional text listing.
        !            31: # Hacked by Guenter Knauf.
        !            32: #
        !            33: use Encode;
        !            34: use Getopt::Std;
        !            35: use MIME::Base64;
        !            36: use strict;
        !            37: use warnings;
        !            38: use vars qw($opt_b $opt_d $opt_f $opt_h $opt_i $opt_k $opt_l $opt_m $opt_n $opt_p $opt_q $opt_s $opt_t $opt_u $opt_v $opt_w);
        !            39: use List::Util;
        !            40: use Text::Wrap;
        !            41: use Time::Local;
        !            42: my $MOD_SHA = "Digest::SHA";
        !            43: eval "require $MOD_SHA";
        !            44: if ($@) {
        !            45:   $MOD_SHA = "Digest::SHA::PurePerl";
        !            46:   eval "require $MOD_SHA";
        !            47: }
        !            48: eval "require LWP::UserAgent";
        !            49: 
        !            50: my %urls = (
        !            51:   'nss' =>
        !            52:     'https://hg.mozilla.org/projects/nss/raw-file/default/lib/ckfw/builtins/certdata.txt',
        !            53:   'central' =>
        !            54:     'https://hg.mozilla.org/mozilla-central/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
        !            55:   'beta' =>
        !            56:     'https://hg.mozilla.org/releases/mozilla-beta/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
        !            57:   'release' =>
        !            58:     'https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
        !            59: );
        !            60: 
        !            61: $opt_d = 'release';
        !            62: 
        !            63: # If the OpenSSL commandline is not in search path you can configure it here!
        !            64: my $openssl = 'openssl';
        !            65: 
        !            66: my $version = '1.28';
        !            67: 
        !            68: $opt_w = 76; # default base64 encoded lines length
        !            69: 
        !            70: # default cert types to include in the output (default is to include CAs which may issue SSL server certs)
        !            71: my $default_mozilla_trust_purposes = "SERVER_AUTH";
        !            72: my $default_mozilla_trust_levels = "TRUSTED_DELEGATOR";
        !            73: $opt_p = $default_mozilla_trust_purposes . ":" . $default_mozilla_trust_levels;
        !            74: 
        !            75: my @valid_mozilla_trust_purposes = (
        !            76:   "DIGITAL_SIGNATURE",
        !            77:   "NON_REPUDIATION",
        !            78:   "KEY_ENCIPHERMENT",
        !            79:   "DATA_ENCIPHERMENT",
        !            80:   "KEY_AGREEMENT",
        !            81:   "KEY_CERT_SIGN",
        !            82:   "CRL_SIGN",
        !            83:   "SERVER_AUTH",
        !            84:   "CLIENT_AUTH",
        !            85:   "CODE_SIGNING",
        !            86:   "EMAIL_PROTECTION",
        !            87:   "IPSEC_END_SYSTEM",
        !            88:   "IPSEC_TUNNEL",
        !            89:   "IPSEC_USER",
        !            90:   "TIME_STAMPING",
        !            91:   "STEP_UP_APPROVED"
        !            92: );
        !            93: 
        !            94: my @valid_mozilla_trust_levels = (
        !            95:   "TRUSTED_DELEGATOR",    # CAs
        !            96:   "NOT_TRUSTED",          # Don't trust these certs.
        !            97:   "MUST_VERIFY_TRUST",    # This explicitly tells us that it ISN'T a CA but is otherwise ok. In other words, this should tell the app to ignore any other sources that claim this is a CA.
        !            98:   "TRUSTED"               # This cert is trusted, but only for itself and not for delegates (i.e. it is not a CA).
        !            99: );
        !           100: 
        !           101: my $default_signature_algorithms = $opt_s = "MD5";
        !           102: 
        !           103: my @valid_signature_algorithms = (
        !           104:   "MD5",
        !           105:   "SHA1",
        !           106:   "SHA256",
        !           107:   "SHA384",
        !           108:   "SHA512"
        !           109: );
        !           110: 
        !           111: $0 =~ s@.*(/|\\)@@;
        !           112: $Getopt::Std::STANDARD_HELP_VERSION = 1;
        !           113: getopts('bd:fhiklmnp:qs:tuvw:');
        !           114: 
        !           115: if(!defined($opt_d)) {
        !           116:     # to make plain "-d" use not cause warnings, and actually still work
        !           117:     $opt_d = 'release';
        !           118: }
        !           119: 
        !           120: # Use predefined URL or else custom URL specified on command line.
        !           121: my $url;
        !           122: if(defined($urls{$opt_d})) {
        !           123:   $url = $urls{$opt_d};
        !           124:   if(!$opt_k && $url !~ /^https:\/\//i) {
        !           125:     die "The URL for '$opt_d' is not HTTPS. Use -k to override (insecure).\n";
        !           126:   }
        !           127: }
        !           128: else {
        !           129:   $url = $opt_d;
        !           130: }
        !           131: 
        !           132: my $curl = `curl -V`;
        !           133: 
        !           134: if ($opt_i) {
        !           135:   print ("=" x 78 . "\n");
        !           136:   print "Script Version                   : $version\n";
        !           137:   print "Perl Version                     : $]\n";
        !           138:   print "Operating System Name            : $^O\n";
        !           139:   print "Getopt::Std.pm Version           : ${Getopt::Std::VERSION}\n";
        !           140:   print "Encode::Encoding.pm Version      : ${Encode::Encoding::VERSION}\n";
        !           141:   print "MIME::Base64.pm Version          : ${MIME::Base64::VERSION}\n";
        !           142:   print "LWP::UserAgent.pm Version        : ${LWP::UserAgent::VERSION}\n" if($LWP::UserAgent::VERSION);
        !           143:   print "LWP.pm Version                   : ${LWP::VERSION}\n" if($LWP::VERSION);
        !           144:   print "Digest::SHA.pm Version           : ${Digest::SHA::VERSION}\n" if ($Digest::SHA::VERSION);
        !           145:   print "Digest::SHA::PurePerl.pm Version : ${Digest::SHA::PurePerl::VERSION}\n" if ($Digest::SHA::PurePerl::VERSION);
        !           146:   print ("=" x 78 . "\n");
        !           147: }
        !           148: 
        !           149: sub warning_message() {
        !           150:   if ( $opt_d =~ m/^risk$/i ) { # Long Form Warning and Exit
        !           151:     print "Warning: Use of this script may pose some risk:\n";
        !           152:     print "\n";
        !           153:     print "  1) If you use HTTP URLs they are subject to a man in the middle attack\n";
        !           154:     print "  2) Default to 'release', but more recent updates may be found in other trees\n";
        !           155:     print "  3) certdata.txt file format may change, lag time to update this script\n";
        !           156:     print "  4) Generally unwise to blindly trust CAs without manual review & verification\n";
        !           157:     print "  5) Mozilla apps use additional security checks aren't represented in certdata\n";
        !           158:     print "  6) Use of this script will make a security engineer grind his teeth and\n";
        !           159:     print "     swear at you.  ;)\n";
        !           160:     exit;
        !           161:   } else { # Short Form Warning
        !           162:     print "Warning: Use of this script may pose some risk, -d risk for more details.\n";
        !           163:   }
        !           164: }
        !           165: 
        !           166: sub HELP_MESSAGE() {
        !           167:   print "Usage:\t${0} [-b] [-d<certdata>] [-f] [-i] [-k] [-l] [-n] [-p<purposes:levels>] [-q] [-s<algorithms>] [-t] [-u] [-v] [-w<l>] [<outputfile>]\n";
        !           168:   print "\t-b\tbackup an existing version of ca-bundle.crt\n";
        !           169:   print "\t-d\tspecify Mozilla tree to pull certdata.txt or custom URL\n";
        !           170:   print "\t\t  Valid names are:\n";
        !           171:   print "\t\t    ", join( ", ", map { ( $_ =~ m/$opt_d/ ) ? "$_ (default)" : "$_" } sort keys %urls ), "\n";
        !           172:   print "\t-f\tforce rebuild even if certdata.txt is current\n";
        !           173:   print "\t-i\tprint version info about used modules\n";
        !           174:   print "\t-k\tallow URLs other than HTTPS, enable HTTP fallback (insecure)\n";
        !           175:   print "\t-l\tprint license info about certdata.txt\n";
        !           176:   print "\t-m\tinclude meta data in output\n";
        !           177:   print "\t-n\tno download of certdata.txt (to use existing)\n";
        !           178:   print wrap("\t","\t\t", "-p\tlist of Mozilla trust purposes and levels for certificates to include in output. Takes the form of a comma separated list of purposes, a colon, and a comma separated list of levels. (default: $default_mozilla_trust_purposes:$default_mozilla_trust_levels)"), "\n";
        !           179:   print "\t\t  Valid purposes are:\n";
        !           180:   print wrap("\t\t    ","\t\t    ", join( ", ", "ALL", @valid_mozilla_trust_purposes ) ), "\n";
        !           181:   print "\t\t  Valid levels are:\n";
        !           182:   print wrap("\t\t    ","\t\t    ", join( ", ", "ALL", @valid_mozilla_trust_levels ) ), "\n";
        !           183:   print "\t-q\tbe really quiet (no progress output at all)\n";
        !           184:   print wrap("\t","\t\t", "-s\tcomma separated list of certificate signatures/hashes to output in plain text mode. (default: $default_signature_algorithms)\n");
        !           185:   print "\t\t  Valid signature algorithms are:\n";
        !           186:   print wrap("\t\t    ","\t\t    ", join( ", ", "ALL", @valid_signature_algorithms ) ), "\n";
        !           187:   print "\t-t\tinclude plain text listing of certificates\n";
        !           188:   print "\t-u\tunlink (remove) certdata.txt after processing\n";
        !           189:   print "\t-v\tbe verbose and print out processed CAs\n";
        !           190:   print "\t-w <l>\twrap base64 output lines after <l> chars (default: ${opt_w})\n";
        !           191:   exit;
        !           192: }
        !           193: 
        !           194: sub VERSION_MESSAGE() {
        !           195:   print "${0} version ${version} running Perl ${]} on ${^O}\n";
        !           196: }
        !           197: 
        !           198: warning_message() unless ($opt_q || $url =~ m/^(ht|f)tps:/i );
        !           199: HELP_MESSAGE() if ($opt_h);
        !           200: 
        !           201: sub report($@) {
        !           202:   my $output = shift;
        !           203: 
        !           204:   print STDERR $output . "\n" unless $opt_q;
        !           205: }
        !           206: 
        !           207: sub is_in_list($@) {
        !           208:   my $target = shift;
        !           209: 
        !           210:   return defined(List::Util::first { $target eq $_ } @_);
        !           211: }
        !           212: 
        !           213: # Parses $param_string as a case insensitive comma separated list with optional whitespace
        !           214: # validates that only allowed parameters are supplied
        !           215: sub parse_csv_param($$@) {
        !           216:   my $description = shift;
        !           217:   my $param_string = shift;
        !           218:   my @valid_values = @_;
        !           219: 
        !           220:   my @values = map {
        !           221:     s/^\s+//;  # strip leading spaces
        !           222:     s/\s+$//;  # strip trailing spaces
        !           223:     uc $_      # return the modified string as upper case
        !           224:   } split( ',', $param_string );
        !           225: 
        !           226:   # Find all values which are not in the list of valid values or "ALL"
        !           227:   my @invalid = grep { !is_in_list($_,"ALL",@valid_values) } @values;
        !           228: 
        !           229:   if ( scalar(@invalid) > 0 ) {
        !           230:     # Tell the user which parameters were invalid and print the standard help message which will exit
        !           231:     print "Error: Invalid ", $description, scalar(@invalid) == 1 ? ": " : "s: ", join( ", ", map { "\"$_\"" } @invalid ), "\n";
        !           232:     HELP_MESSAGE();
        !           233:   }
        !           234: 
        !           235:   @values = @valid_values if ( is_in_list("ALL",@values) );
        !           236: 
        !           237:   return @values;
        !           238: }
        !           239: 
        !           240: sub sha256 {
        !           241:   my $result;
        !           242:   if ($Digest::SHA::VERSION || $Digest::SHA::PurePerl::VERSION) {
        !           243:     open(FILE, $_[0]) or die "Can't open '$_[0]': $!";
        !           244:     binmode(FILE);
        !           245:     $result = $MOD_SHA->new(256)->addfile(*FILE)->hexdigest;
        !           246:     close(FILE);
        !           247:   } else {
        !           248:     # Use OpenSSL command if Perl Digest::SHA modules not available
        !           249:     $result = `"$openssl" dgst -r -sha256 "$_[0]"`;
        !           250:     $result =~ s/^([0-9a-f]{64}) .+/$1/is;
        !           251:   }
        !           252:   return $result;
        !           253: }
        !           254: 
        !           255: 
        !           256: sub oldhash {
        !           257:   my $hash = "";
        !           258:   open(C, "<$_[0]") || return 0;
        !           259:   while(<C>) {
        !           260:     chomp;
        !           261:     if($_ =~ /^\#\# SHA256: (.*)/) {
        !           262:       $hash = $1;
        !           263:       last;
        !           264:     }
        !           265:   }
        !           266:   close(C);
        !           267:   return $hash;
        !           268: }
        !           269: 
        !           270: if ( $opt_p !~ m/:/ ) {
        !           271:   print "Error: Mozilla trust identifier list must include both purposes and levels\n";
        !           272:   HELP_MESSAGE();
        !           273: }
        !           274: 
        !           275: (my $included_mozilla_trust_purposes_string, my $included_mozilla_trust_levels_string) = split( ':', $opt_p );
        !           276: my @included_mozilla_trust_purposes = parse_csv_param( "trust purpose", $included_mozilla_trust_purposes_string, @valid_mozilla_trust_purposes );
        !           277: my @included_mozilla_trust_levels = parse_csv_param( "trust level", $included_mozilla_trust_levels_string, @valid_mozilla_trust_levels );
        !           278: 
        !           279: my @included_signature_algorithms = parse_csv_param( "signature algorithm", $opt_s, @valid_signature_algorithms );
        !           280: 
        !           281: sub should_output_cert(%) {
        !           282:   my %trust_purposes_by_level = @_;
        !           283: 
        !           284:   foreach my $level (@included_mozilla_trust_levels) {
        !           285:     # for each level we want to output, see if any of our desired purposes are included
        !           286:     return 1 if ( defined( List::Util::first { is_in_list( $_, @included_mozilla_trust_purposes ) } @{$trust_purposes_by_level{$level}} ) );
        !           287:   }
        !           288: 
        !           289:   return 0;
        !           290: }
        !           291: 
        !           292: my $crt = $ARGV[0] || 'ca-bundle.crt';
        !           293: (my $txt = $url) =~ s@(.*/|\?.*)@@g;
        !           294: 
        !           295: my $stdout = $crt eq '-';
        !           296: my $resp;
        !           297: my $fetched;
        !           298: 
        !           299: my $oldhash = oldhash($crt);
        !           300: 
        !           301: report "SHA256 of old file: $oldhash";
        !           302: 
        !           303: if(!$opt_n) {
        !           304:   report "Downloading $txt ...";
        !           305: 
        !           306:   # If we have an HTTPS URL then use curl
        !           307:   if($url =~ /^https:\/\//i) {
        !           308:     if($curl) {
        !           309:       if($curl =~ /^Protocols:.* https( |$)/m) {
        !           310:         report "Get certdata with curl!";
        !           311:         my $proto = !$opt_k ? "--proto =https" : "";
        !           312:         my $quiet = $opt_q ? "-s" : "";
        !           313:         my @out = `curl -w %{response_code} $proto $quiet -o "$txt" "$url"`;
        !           314:         if(!$? && @out && $out[0] == 200) {
        !           315:           $fetched = 1;
        !           316:           report "Downloaded $txt";
        !           317:         }
        !           318:         else {
        !           319:           report "Failed downloading via HTTPS with curl";
        !           320:           if(-e $txt && !unlink($txt)) {
        !           321:             report "Failed to remove '$txt': $!";
        !           322:           }
        !           323:         }
        !           324:       }
        !           325:       else {
        !           326:         report "curl lacks https support";
        !           327:       }
        !           328:     }
        !           329:     else {
        !           330:       report "curl not found";
        !           331:     }
        !           332:   }
        !           333: 
        !           334:   # If nothing was fetched then use LWP
        !           335:   if(!$fetched) {
        !           336:     if($url =~ /^https:\/\//i) {
        !           337:       report "Falling back to HTTP";
        !           338:       $url =~ s/^https:\/\//http:\/\//i;
        !           339:     }
        !           340:     if(!$opt_k) {
        !           341:       report "URLs other than HTTPS are disabled by default, to enable use -k";
        !           342:       exit 1;
        !           343:     }
        !           344:     report "Get certdata with LWP!";
        !           345:     if(!defined(${LWP::UserAgent::VERSION})) {
        !           346:       report "LWP is not available (LWP::UserAgent not found)";
        !           347:       exit 1;
        !           348:     }
        !           349:     my $ua  = new LWP::UserAgent(agent => "$0/$version");
        !           350:     $ua->env_proxy();
        !           351:     $resp = $ua->mirror($url, $txt);
        !           352:     if($resp && $resp->code eq '304') {
        !           353:       report "Not modified";
        !           354:       exit 0 if -e $crt && !$opt_f;
        !           355:     }
        !           356:     else {
        !           357:       $fetched = 1;
        !           358:       report "Downloaded $txt";
        !           359:     }
        !           360:     if(!$resp || $resp->code !~ /^(?:200|304)$/) {
        !           361:       report "Unable to download latest data: "
        !           362:         . ($resp? $resp->code . ' - ' . $resp->message : "LWP failed");
        !           363:       exit 1 if -e $crt || ! -r $txt;
        !           364:     }
        !           365:   }
        !           366: }
        !           367: 
        !           368: my $filedate = $resp ? $resp->last_modified : (stat($txt))[9];
        !           369: my $datesrc = "as of";
        !           370: if(!$filedate) {
        !           371:     # mxr.mozilla.org gave us a time, hg.mozilla.org does not!
        !           372:     $filedate = time();
        !           373:     $datesrc="downloaded on";
        !           374: }
        !           375: 
        !           376: # get the hash from the download file
        !           377: my $newhash= sha256($txt);
        !           378: 
        !           379: if(!$opt_f && $oldhash eq $newhash) {
        !           380:     report "Downloaded file identical to previous run\'s source file. Exiting";
        !           381:     if($opt_u && -e $txt && !unlink($txt)) {
        !           382:         report "Failed to remove $txt: $!\n";
        !           383:     }
        !           384:     exit;
        !           385: }
        !           386: 
        !           387: report "SHA256 of new file: $newhash";
        !           388: 
        !           389: my $currentdate = scalar gmtime($filedate);
        !           390: 
        !           391: my $format = $opt_t ? "plain text and " : "";
        !           392: if( $stdout ) {
        !           393:     open(CRT, '> -') or die "Couldn't open STDOUT: $!\n";
        !           394: } else {
        !           395:     open(CRT,">$crt.~") or die "Couldn't open $crt.~: $!\n";
        !           396: }
        !           397: print CRT <<EOT;
        !           398: ##
        !           399: ## Bundle of CA Root Certificates
        !           400: ##
        !           401: ## Certificate data from Mozilla ${datesrc}: ${currentdate} GMT
        !           402: ##
        !           403: ## This is a bundle of X.509 certificates of public Certificate Authorities
        !           404: ## (CA). These were automatically extracted from Mozilla's root certificates
        !           405: ## file (certdata.txt).  This file can be found in the mozilla source tree:
        !           406: ## ${url}
        !           407: ##
        !           408: ## It contains the certificates in ${format}PEM format and therefore
        !           409: ## can be directly used with curl / libcurl / php_curl, or with
        !           410: ## an Apache+mod_ssl webserver for SSL client authentication.
        !           411: ## Just configure this file as the SSLCACertificateFile.
        !           412: ##
        !           413: ## Conversion done with mk-ca-bundle.pl version $version.
        !           414: ## SHA256: $newhash
        !           415: ##
        !           416: 
        !           417: EOT
        !           418: 
        !           419: report "Processing  '$txt' ...";
        !           420: my $caname;
        !           421: my $certnum = 0;
        !           422: my $skipnum = 0;
        !           423: my $start_of_cert = 0;
        !           424: my @precert;
        !           425: my $cka_value;
        !           426: my $valid = 1;
        !           427: 
        !           428: open(TXT,"$txt") or die "Couldn't open $txt: $!\n";
        !           429: while (<TXT>) {
        !           430:   if (/\*\*\*\*\* BEGIN LICENSE BLOCK \*\*\*\*\*/) {
        !           431:     print CRT;
        !           432:     print if ($opt_l);
        !           433:     while (<TXT>) {
        !           434:       print CRT;
        !           435:       print if ($opt_l);
        !           436:       last if (/\*\*\*\*\* END LICENSE BLOCK \*\*\*\*\*/);
        !           437:     }
        !           438:   }
        !           439:   elsif(/^# (Issuer|Serial Number|Subject|Not Valid Before|Not Valid After |Fingerprint \(MD5\)|Fingerprint \(SHA1\)):/) {
        !           440:       push @precert, $_;
        !           441:       $valid = 1;
        !           442:       next;
        !           443:   }
        !           444:   elsif(/^#|^\s*$/) {
        !           445:       undef @precert;
        !           446:       next;
        !           447:   }
        !           448:   chomp;
        !           449: 
        !           450:   # Example:
        !           451:   # CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL
        !           452:   # \062\060\060\066\061\067\060\060\060\060\060\060\132
        !           453:   # END
        !           454: 
        !           455:   if (/^CKA_NSS_SERVER_DISTRUST_AFTER (CK_BBOOL CK_FALSE|MULTILINE_OCTAL)/) {
        !           456:       if($1 eq "MULTILINE_OCTAL") {
        !           457:           my @timestamp;
        !           458:           while (<TXT>) {
        !           459:               last if (/^END/);
        !           460:               chomp;
        !           461:               my @octets = split(/\\/);
        !           462:               shift @octets;
        !           463:               for (@octets) {
        !           464:                   push @timestamp, chr(oct);
        !           465:               }
        !           466:           }
        !           467:           # A trailing Z in the timestamp signifies UTC
        !           468:           if($timestamp[12] ne "Z") {
        !           469:               report "distrust date stamp is not using UTC";
        !           470:           }
        !           471:           # Example date: 200617000000Z
        !           472:           # Means 2020-06-17 00:00:00 UTC
        !           473:           my $distrustat =
        !           474:             timegm($timestamp[10] . $timestamp[11], # second
        !           475:                    $timestamp[8] . $timestamp[9],   # minute
        !           476:                    $timestamp[6] . $timestamp[7],   # hour
        !           477:                    $timestamp[4] . $timestamp[5],   # day
        !           478:                    ($timestamp[2] . $timestamp[3]) - 1, # month
        !           479:                    "20" . $timestamp[0] . $timestamp[1]); # year
        !           480:           if(time >= $distrustat) {
        !           481:               # not trusted anymore
        !           482:               $skipnum++;
        !           483:               report "Skipping: $caname is not trusted anymore" if ($opt_v);
        !           484:               $valid = 0;
        !           485:           }
        !           486:           else {
        !           487:               # still trusted
        !           488:           }
        !           489:       }
        !           490:       next;
        !           491:   }
        !           492: 
        !           493:   # this is a match for the start of a certificate
        !           494:   if (/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/) {
        !           495:     $start_of_cert = 1
        !           496:   }
        !           497:   if ($start_of_cert && /^CKA_LABEL UTF8 \"(.*)\"/) {
        !           498:     $caname = $1;
        !           499:   }
        !           500:   my %trust_purposes_by_level;
        !           501:   if ($start_of_cert && /^CKA_VALUE MULTILINE_OCTAL/) {
        !           502:     $cka_value="";
        !           503:     while (<TXT>) {
        !           504:       last if (/^END/);
        !           505:       chomp;
        !           506:       my @octets = split(/\\/);
        !           507:       shift @octets;
        !           508:       for (@octets) {
        !           509:         $cka_value .= chr(oct);
        !           510:       }
        !           511:     }
        !           512:   }
        !           513:   if(/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/ && $valid) {
        !           514:     # now scan the trust part to determine how we should trust this cert
        !           515:     while (<TXT>) {
        !           516:       last if (/^#/);
        !           517:       if (/^CKA_TRUST_([A-Z_]+)\s+CK_TRUST\s+CKT_NSS_([A-Z_]+)\s*$/) {
        !           518:         if ( !is_in_list($1,@valid_mozilla_trust_purposes) ) {
        !           519:           report "Warning: Unrecognized trust purpose for cert: $caname. Trust purpose: $1. Trust Level: $2";
        !           520:         } elsif ( !is_in_list($2,@valid_mozilla_trust_levels) ) {
        !           521:           report "Warning: Unrecognized trust level for cert: $caname. Trust purpose: $1. Trust Level: $2";
        !           522:         } else {
        !           523:           push @{$trust_purposes_by_level{$2}}, $1;
        !           524:         }
        !           525:       }
        !           526:     }
        !           527: 
        !           528:     if ( !should_output_cert(%trust_purposes_by_level) ) {
        !           529:       $skipnum ++;
        !           530:       report "Skipping: $caname" if ($opt_v);
        !           531:     } else {
        !           532:       my $data = $cka_value;
        !           533:       $cka_value = "";
        !           534: 
        !           535:       if(!length($data)) {
        !           536:           # if empty, skip
        !           537:           next;
        !           538:       }
        !           539:       my $encoded = MIME::Base64::encode_base64($data, '');
        !           540:       $encoded =~ s/(.{1,${opt_w}})/$1\n/g;
        !           541:       my $pem = "-----BEGIN CERTIFICATE-----\n"
        !           542:               . $encoded
        !           543:               . "-----END CERTIFICATE-----\n";
        !           544:       print CRT "\n$caname\n";
        !           545:       print CRT @precert if($opt_m);
        !           546:       my $maxStringLength = length(decode('UTF-8', $caname, Encode::FB_CROAK | Encode::LEAVE_SRC));
        !           547:       if ($opt_t) {
        !           548:         foreach my $key (keys %trust_purposes_by_level) {
        !           549:            my $string = $key . ": " . join(", ", @{$trust_purposes_by_level{$key}});
        !           550:            $maxStringLength = List::Util::max( length($string), $maxStringLength );
        !           551:            print CRT $string . "\n";
        !           552:         }
        !           553:       }
        !           554:       print CRT ("=" x $maxStringLength . "\n");
        !           555:       if (!$opt_t) {
        !           556:         print CRT $pem;
        !           557:       } else {
        !           558:         my $pipe = "";
        !           559:         foreach my $hash (@included_signature_algorithms) {
        !           560:           $pipe = "|$openssl x509 -" . $hash . " -fingerprint -noout -inform PEM";
        !           561:           if (!$stdout) {
        !           562:             $pipe .= " >> $crt.~";
        !           563:             close(CRT) or die "Couldn't close $crt.~: $!";
        !           564:           }
        !           565:           open(TMP, $pipe) or die "Couldn't open openssl pipe: $!";
        !           566:           print TMP $pem;
        !           567:           close(TMP) or die "Couldn't close openssl pipe: $!";
        !           568:           if (!$stdout) {
        !           569:             open(CRT, ">>$crt.~") or die "Couldn't open $crt.~: $!";
        !           570:           }
        !           571:         }
        !           572:         $pipe = "|$openssl x509 -text -inform PEM";
        !           573:         if (!$stdout) {
        !           574:           $pipe .= " >> $crt.~";
        !           575:           close(CRT) or die "Couldn't close $crt.~: $!";
        !           576:         }
        !           577:         open(TMP, $pipe) or die "Couldn't open openssl pipe: $!";
        !           578:         print TMP $pem;
        !           579:         close(TMP) or die "Couldn't close openssl pipe: $!";
        !           580:         if (!$stdout) {
        !           581:           open(CRT, ">>$crt.~") or die "Couldn't open $crt.~: $!";
        !           582:         }
        !           583:       }
        !           584:       report "Parsing: $caname" if ($opt_v);
        !           585:       $certnum ++;
        !           586:       $start_of_cert = 0;
        !           587:     }
        !           588:     undef @precert;
        !           589:   }
        !           590: 
        !           591: }
        !           592: close(TXT) or die "Couldn't close $txt: $!\n";
        !           593: close(CRT) or die "Couldn't close $crt.~: $!\n";
        !           594: unless( $stdout ) {
        !           595:     if ($opt_b && -e $crt) {
        !           596:         my $bk = 1;
        !           597:         while (-e "$crt.~${bk}~") {
        !           598:             $bk++;
        !           599:         }
        !           600:         rename $crt, "$crt.~${bk}~" or die "Failed to create backup $crt.~$bk}~: $!\n";
        !           601:     } elsif( -e $crt ) {
        !           602:         unlink( $crt ) or die "Failed to remove $crt: $!\n";
        !           603:     }
        !           604:     rename "$crt.~", $crt or die "Failed to rename $crt.~ to $crt: $!\n";
        !           605: }
        !           606: if($opt_u && -e $txt && !unlink($txt)) {
        !           607:   report "Failed to remove $txt: $!\n";
        !           608: }
        !           609: report "Done ($certnum CA certs processed, $skipnum skipped).";

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>