Imported Upstream version 7.53.1
[platform/upstream/curl.git] / lib / mk-ca-bundle.pl
index 01a552e..9574f1d 100755 (executable)
 # dependency is the OpenSSL commandline tool for optional text listing.
 # Hacked by Guenter Knauf.
 #
+use Encode;
 use Getopt::Std;
 use MIME::Base64;
-use LWP::UserAgent;
 use strict;
-use vars qw($opt_b $opt_d $opt_f $opt_h $opt_i $opt_l $opt_m $opt_n $opt_p $opt_q $opt_s $opt_t $opt_u $opt_v $opt_w);
+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);
 use List::Util;
 use Text::Wrap;
 my $MOD_SHA = "Digest::SHA";
@@ -43,18 +43,19 @@ if ($@) {
   $MOD_SHA = "Digest::SHA::PurePerl";
   eval "require $MOD_SHA";
 }
+eval "require LWP::UserAgent";
 
 my %urls = (
   'nss' =>
-    'http://hg.mozilla.org/projects/nss/raw-file/tip/lib/ckfw/builtins/certdata.txt',
+    'https://hg.mozilla.org/projects/nss/raw-file/tip/lib/ckfw/builtins/certdata.txt',
   'central' =>
-    'http://hg.mozilla.org/mozilla-central/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
+    'https://hg.mozilla.org/mozilla-central/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
   'aurora' =>
-    'http://hg.mozilla.org/releases/mozilla-aurora/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
+    'https://hg.mozilla.org/releases/mozilla-aurora/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
   'beta' =>
-    'http://hg.mozilla.org/releases/mozilla-beta/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
+    'https://hg.mozilla.org/releases/mozilla-beta/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
   'release' =>
-    'http://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
+    'https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt',
 );
 
 $opt_d = 'release';
@@ -62,7 +63,7 @@ $opt_d = 'release';
 # If the OpenSSL commandline is not in search path you can configure it here!
 my $openssl = 'openssl';
 
-my $version = '1.26';
+my $version = '1.27';
 
 $opt_w = 76; # default base64 encoded lines length
 
@@ -109,7 +110,7 @@ my @valid_signature_algorithms = (
 
 $0 =~ s@.*(/|\\)@@;
 $Getopt::Std::STANDARD_HELP_VERSION = 1;
-getopts('bd:fhilmnp:qs:tuvw:');
+getopts('bd:fhiklmnp:qs:tuvw:');
 
 if(!defined($opt_d)) {
     # to make plain "-d" use not cause warnings, and actually still work
@@ -117,7 +118,16 @@ if(!defined($opt_d)) {
 }
 
 # Use predefined URL or else custom URL specified on command line.
-my $url = ( defined( $urls{$opt_d} ) ) ? $urls{$opt_d} : $opt_d;
+my $url;
+if(defined($urls{$opt_d})) {
+  $url = $urls{$opt_d};
+  if(!$opt_k && $url !~ /^https:\/\//i) {
+    die "The URL for '$opt_d' is not HTTPS. Use -k to override (insecure).\n";
+  }
+}
+else {
+  $url = $opt_d;
+}
 
 my $curl = `curl -V`;
 
@@ -128,8 +138,8 @@ if ($opt_i) {
   print "Operating System Name            : $^O\n";
   print "Getopt::Std.pm Version           : ${Getopt::Std::VERSION}\n";
   print "MIME::Base64.pm Version          : ${MIME::Base64::VERSION}\n";
-  print "LWP::UserAgent.pm Version        : ${LWP::UserAgent::VERSION}\n";
-  print "LWP.pm Version                   : ${LWP::VERSION}\n";
+  print "LWP::UserAgent.pm Version        : ${LWP::UserAgent::VERSION}\n" if($LWP::UserAgent::VERSION);
+  print "LWP.pm Version                   : ${LWP::VERSION}\n" if($LWP::VERSION);
   print "Digest::SHA.pm Version           : ${Digest::SHA::VERSION}\n" if ($Digest::SHA::VERSION);
   print "Digest::SHA::PurePerl.pm Version : ${Digest::SHA::PurePerl::VERSION}\n" if ($Digest::SHA::PurePerl::VERSION);
   print ("=" x 78 . "\n");
@@ -139,7 +149,7 @@ sub warning_message() {
   if ( $opt_d =~ m/^risk$/i ) { # Long Form Warning and Exit
     print "Warning: Use of this script may pose some risk:\n";
     print "\n";
-    print "  1) Using http is subject to man in the middle attack of certdata content\n";
+    print "  1) If you use HTTP URLs they are subject to a man in the middle attack\n";
     print "  2) Default to 'release', but more recent updates may be found in other trees\n";
     print "  3) certdata.txt file format may change, lag time to update this script\n";
     print "  4) Generally unwise to blindly trust CAs without manual review & verification\n";
@@ -153,13 +163,14 @@ sub warning_message() {
 }
 
 sub HELP_MESSAGE() {
-  print "Usage:\t${0} [-b] [-d<certdata>] [-f] [-i] [-l] [-n] [-p<purposes:levels>] [-q] [-s<algorithms>] [-t] [-u] [-v] [-w<l>] [<outputfile>]\n";
+  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";
   print "\t-b\tbackup an existing version of ca-bundle.crt\n";
   print "\t-d\tspecify Mozilla tree to pull certdata.txt or custom URL\n";
   print "\t\t  Valid names are:\n";
   print "\t\t    ", join( ", ", map { ( $_ =~ m/$opt_d/ ) ? "$_ (default)" : "$_" } sort keys %urls ), "\n";
   print "\t-f\tforce rebuild even if certdata.txt is current\n";
   print "\t-i\tprint version info about used modules\n";
+  print "\t-k\tallow URLs other than HTTPS, enable HTTP fallback (insecure)\n";
   print "\t-l\tprint license info about certdata.txt\n";
   print "\t-m\tinclude meta data in output\n";
   print "\t-n\tno download of certdata.txt (to use existing)\n";
@@ -225,33 +236,34 @@ sub parse_csv_param($$@) {
   return @values;
 }
 
-sub sha1 {
+sub sha256 {
   my $result;
   if ($Digest::SHA::VERSION || $Digest::SHA::PurePerl::VERSION) {
     open(FILE, $_[0]) or die "Can't open '$_[0]': $!";
     binmode(FILE);
-    $result = $MOD_SHA->new(1)->addfile(*FILE)->hexdigest;
+    $result = $MOD_SHA->new(256)->addfile(*FILE)->hexdigest;
     close(FILE);
   } else {
     # Use OpenSSL command if Perl Digest::SHA modules not available
-    $result = (split(/ |\r|\n/,`$openssl dgst -sha1 $_[0]`))[1];
+    $result = `"$openssl" dgst -r -sha256 "$_[0]"`;
+    $result =~ s/^([0-9a-f]{64}) .+/$1/is;
   }
   return $result;
 }
 
 
-sub oldsha1 {
-  my $sha1 = "";
+sub oldhash {
+  my $hash = "";
   open(C, "<$_[0]") || return 0;
   while(<C>) {
     chomp;
-    if($_ =~ /^\#\# SHA1: (.*)/) {
-      $sha1 = $1;
+    if($_ =~ /^\#\# SHA256: (.*)/) {
+      $hash = $1;
       last;
     }
   }
   close(C);
-  return $sha1;
+  return $hash;
 }
 
 if ( $opt_p !~ m/:/ ) {
@@ -283,39 +295,72 @@ my $stdout = $crt eq '-';
 my $resp;
 my $fetched;
 
-my $oldsha1 = oldsha1($crt);
+my $oldhash = oldhash($crt);
 
-report "SHA1 of old file: $oldsha1";
+report "SHA256 of old file: $oldhash";
 
-report "Downloading '$txt' ...";
+if(!$opt_n) {
+  report "Downloading $txt ...";
 
-if($curl && !$opt_n) {
-  my $https = $url;
-  $https =~ s/^http:/https:/;
-  report "Get certdata over HTTPS with curl!";
-  my $quiet = $opt_q ? "-s" : "";
-  my @out = `curl -w %{response_code} $quiet -O $https`;
-  if(@out && $out[0] == 200) {
-    $fetched = 1;
-  } else {
-    report "Failed downloading HTTPS with curl, trying HTTP with LWP";
+  # If we have an HTTPS URL then use curl
+  if($url =~ /^https:\/\//i) {
+    if($curl) {
+      if($curl =~ /^Protocols:.* https( |$)/m) {
+        report "Get certdata with curl!";
+        my $proto = !$opt_k ? "--proto =https" : "";
+        my $quiet = $opt_q ? "-s" : "";
+        my @out = `curl -w %{response_code} $proto $quiet -o "$txt" "$url"`;
+        if(@out && $out[0] == 200) {
+          $fetched = 1;
+          report "Downloaded $txt";
+        }
+        else {
+          report "Failed downloading via HTTPS with curl";
+          if(-e $txt && !unlink($txt)) {
+            report "Failed to remove '$txt': $!";
+          }
+        }
+      }
+      else {
+        report "curl lacks https support";
+      }
+    }
+    else {
+      report "curl not found";
+    }
   }
-}
 
-unless ($fetched || ($opt_n and -e $txt)) {
-  my $ua  = new LWP::UserAgent(agent => "$0/$version");
-  $ua->env_proxy();
-  $resp = $ua->mirror($url, $txt);
-  if ($resp && $resp->code eq '304') {
-    report "Not modified";
-    exit 0 if -e $crt && !$opt_f;
-  } else {
+  # If nothing was fetched then use LWP
+  if(!$fetched) {
+    if($url =~ /^https:\/\//i) {
+      report "Falling back to HTTP";
+      $url =~ s/^https:\/\//http:\/\//i;
+    }
+    if(!$opt_k) {
+      report "URLs other than HTTPS are disabled by default, to enable use -k";
+      exit 1;
+    }
+    report "Get certdata with LWP!";
+    if(!defined(${LWP::UserAgent::VERSION})) {
+      report "LWP is not available (LWP::UserAgent not found)";
+      exit 1;
+    }
+    my $ua  = new LWP::UserAgent(agent => "$0/$version");
+    $ua->env_proxy();
+    $resp = $ua->mirror($url, $txt);
+    if($resp && $resp->code eq '304') {
+      report "Not modified";
+      exit 0 if -e $crt && !$opt_f;
+    }
+    else {
       $fetched = 1;
-  }
-  if( !$resp || $resp->code !~ /^(?:200|304)$/ ) {
+      report "Downloaded $txt";
+    }
+    if(!$resp || $resp->code !~ /^(?:200|304)$/) {
       report "Unable to download latest data: "
         . ($resp? $resp->code . ' - ' . $resp->message : "LWP failed");
       exit 1 if -e $crt || ! -r $txt;
+    }
   }
 }
 
@@ -328,14 +373,14 @@ if(!$filedate) {
 }
 
 # get the hash from the download file
-my $newsha1= sha1($txt);
+my $newhash= sha256($txt);
 
-if(!$opt_f && $oldsha1 eq $newsha1) {
+if(!$opt_f && $oldhash eq $newhash) {
     report "Downloaded file identical to previous run\'s source file. Exiting";
     exit;
 }
 
-report "SHA1 of new file: $newsha1";
+report "SHA256 of new file: $newhash";
 
 my $currentdate = scalar gmtime($filedate);
 
@@ -349,7 +394,7 @@ print CRT <<EOT;
 ##
 ## Bundle of CA Root Certificates
 ##
-## Certificate data from Mozilla ${datesrc}: ${currentdate}
+## Certificate data from Mozilla ${datesrc}: ${currentdate} GMT
 ##
 ## This is a bundle of X.509 certificates of public Certificate Authorities
 ## (CA). These were automatically extracted from Mozilla's root certificates
@@ -362,7 +407,7 @@ print CRT <<EOT;
 ## Just configure this file as the SSLCACertificateFile.
 ##
 ## Conversion done with mk-ca-bundle.pl version $version.
-## SHA1: $newsha1
+## SHA256: $newhash
 ##
 
 EOT
@@ -443,7 +488,7 @@ while (<TXT>) {
               . "-----END CERTIFICATE-----\n";
       print CRT "\n$caname\n";
       print CRT @precert if($opt_m);
-      my $maxStringLength = length($caname);
+      my $maxStringLength = length(decode('UTF-8', $caname, Encode::FB_CROAK));
       if ($opt_t) {
         foreach my $key (keys %trust_purposes_by_level) {
            my $string = $key . ": " . join(", ", @{$trust_purposes_by_level{$key}});
@@ -503,5 +548,7 @@ unless( $stdout ) {
     }
     rename "$crt.~", $crt or die "Failed to rename $crt.~ to $crt: $!\n";
 }
-unlink $txt if ($opt_u);
+if($opt_u && -e $txt && !unlink($txt)) {
+  report "Failed to remove $txt: $!\n";
+}
 report "Done ($certnum CA certs processed, $skipnum skipped).";