From 1ad2bdcf110266c33eea70b895cb8c150eeac790 Mon Sep 17 00:00:00 2001 From: Jay Satiro Date: Thu, 15 Sep 2016 02:26:56 -0400 Subject: [PATCH] mk-ca-bundle: Change URL retrieval to HTTPS-only by default - Change all predefined Mozilla URLs to HTTPS (Gregory Szorc). - New option -k to allow URLs other than HTTPS and enable HTTP fallback. Prior to this change the default URL retrieval mode was to fall back to HTTP if HTTPS didn't work. Reported-by: Gregory Szorc Closes #1012 --- lib/mk-ca-bundle.pl | 119 ++++++++++++++++++++++++++++++-------------- 1 file changed, 82 insertions(+), 37 deletions(-) diff --git a/lib/mk-ca-bundle.pl b/lib/mk-ca-bundle.pl index 34497230d..38e7ff73c 100755 --- a/lib/mk-ca-bundle.pl +++ b/lib/mk-ca-bundle.pl @@ -32,9 +32,8 @@ # 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 +42,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 +62,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 +109,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 +117,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 +137,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 +148,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 +162,14 @@ sub warning_message() { } sub HELP_MESSAGE() { - print "Usage:\t${0} [-b] [-d] [-f] [-i] [-l] [-n] [-p] [-q] [-s] [-t] [-u] [-v] [-w] []\n"; + print "Usage:\t${0} [-b] [-d] [-f] [-i] [-k] [-l] [-n] [-p] [-q] [-s] [-t] [-u] [-v] [-w] []\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"; @@ -287,35 +297,68 @@ my $oldhash = oldhash($crt); 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; + } } } @@ -503,5 +546,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).";