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