diff options
author | Natanael Copa <ncopa@alpinelinux.org> | 2020-02-06 09:59:21 +0000 |
---|---|---|
committer | Natanael Copa <ncopa@alpinelinux.org> | 2020-03-05 16:00:12 +0000 |
commit | 7363758e1d81ca25dace4597c1bcf0c92119159c (patch) | |
tree | e4771e6d864b20c7b92b1fccbf758f30366b19e0 /main/ca-certificates | |
parent | f2104c8e2e434343eedb65e6eb52db215ad1d361 (diff) | |
download | aports-7363758e1d81ca25dace4597c1bcf0c92119159c.tar.bz2 aports-7363758e1d81ca25dace4597c1bcf0c92119159c.tar.xz |
main/ca-certificates: ditch python dep, symlink ca-certificates.crt
replace the python script with a perl+shell script to extract the certs.
This helps us to avoid pull in python when bootstrapping.
rename ca-certificates-cacert to ca-certificates-bundle, which is a
better name for the precompiled bundle.
We also ship a pregenerated ca-certificates.crt file and a
/etc/ssl/cert.pem symlink. (fixes #10678)
Diffstat (limited to 'main/ca-certificates')
3 files changed, 921 insertions, 13 deletions
diff --git a/main/ca-certificates/0001-update-ca-fix-compiler-warning.patch b/main/ca-certificates/0001-update-ca-fix-compiler-warning.patch new file mode 100644 index 0000000000..9630cf7fd6 --- /dev/null +++ b/main/ca-certificates/0001-update-ca-fix-compiler-warning.patch @@ -0,0 +1,25 @@ +From 3184fe80e403b9dc6d5fe3b7ebcd9d375363e2e4 Mon Sep 17 00:00:00 2001 +From: Natanael Copa <ncopa@alpinelinux.org> +Date: Wed, 5 Feb 2020 14:42:38 +0100 +Subject: [PATCH 1/3] update-ca: fix compiler warning + +--- + update-ca.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/update-ca.c b/update-ca.c +index 7bb4f1b..2b3195b 100644 +--- a/update-ca.c ++++ b/update-ca.c +@@ -330,7 +330,7 @@ int main(int a, char **v) + free(tmpfile); + + /* Execute run-parts */ +- static const char *run_parts_args[] = { "run-parts", RUNPARTSDIR, 0 }; ++ static char *const run_parts_args[] = { "run-parts", RUNPARTSDIR, 0 }; + execve("/usr/bin/run-parts", run_parts_args, NULL); + execve("/bin/run-parts", run_parts_args, NULL); + perror("run-parts"); +-- +2.25.0 + diff --git a/main/ca-certificates/0002-replace-python-script-with-perl-script.patch b/main/ca-certificates/0002-replace-python-script-with-perl-script.patch new file mode 100644 index 0000000000..fe7e3d98b9 --- /dev/null +++ b/main/ca-certificates/0002-replace-python-script-with-perl-script.patch @@ -0,0 +1,874 @@ +From 6674063331cc37a6a496e44577d9be434cbfc9a2 Mon Sep 17 00:00:00 2001 +From: Natanael Copa <ncopa@alpinelinux.org> +Date: Wed, 5 Feb 2020 15:58:32 +0100 +Subject: [PATCH 2/3] replace python script with perl script + +we need ca-certificates when bootstrapping new architectures. Avoid use +of python to reduce number of dependencies when bootstrapping. + +So use mk-ca-bundle.pl script from curl, and add a small shell script +that splits the bundle to separate .crt files, similar way that the +python script did. +--- + .gitignore | 1 + + Makefile | 11 +- + certdata2pem.py | 155 ------------ + mk-ca-bundle.pl | 604 +++++++++++++++++++++++++++++++++++++++++++++ + split-ca-bundle.sh | 30 +++ + 5 files changed, 642 insertions(+), 159 deletions(-) + delete mode 100644 certdata2pem.py + create mode 100644 mk-ca-bundle.pl + create mode 100644 split-ca-bundle.sh + +diff --git a/.gitignore b/.gitignore +index 8878f38..6f5e9fe 100644 +--- a/.gitignore ++++ b/.gitignore +@@ -2,3 +2,4 @@ update-ca-certificates + c_rehash + certdata.stamp + *.crt ++*.pem +diff --git a/Makefile b/Makefile +index 3eb6672..c688d73 100644 +--- a/Makefile ++++ b/Makefile +@@ -1,4 +1,4 @@ +-PYTHON := python3 ++PERL := perl + + all: update-ca-certificates c_rehash certdata.stamp + +@@ -8,8 +8,11 @@ update-ca-certificates: update-ca.c + c_rehash: c_rehash.c + ${CC} ${CFLAGS} -o $@ c_rehash.c -lcrypto ${LDFLAGS} + +-certdata.stamp: +- ${PYTHON} certdata2pem.py ++cert.pem: mk-ca-bundle.pl ++ ${PERL} mk-ca-bundle.pl -n -w 64 $@ ++ ++certdata.stamp: cert.pem split-ca-bundle.sh ++ ${SHELL} split-ca-bundle.sh < cert.pem + touch $@ + + install: all +@@ -29,7 +32,7 @@ install: all + install -m755 c_rehash ${DESTDIR}/usr/bin + + clean: +- rm -rf update-ca-certificates c_rehash certdata.stamp *.crt ++ rm -rf update-ca-certificates c_rehash certdata.stamp *.crt cert.pem + + # https://hg.mozilla.org/mozilla-central/file/tip/security/nss/lib/ckfw/builtins/certdata.txt + update: +diff --git a/certdata2pem.py b/certdata2pem.py +deleted file mode 100644 +index f91422b..0000000 +--- a/certdata2pem.py ++++ /dev/null +@@ -1,155 +0,0 @@ +-#!/usr/bin/python +-# vim:set et sw=4: +-# +-# certdata2pem.py - splits certdata.txt into multiple files +-# +-# Copyright (C) 2009 Philipp Kern <pkern@debian.org> +-# +-# This program is free software; you can redistribute it and/or modify +-# it under the terms of the GNU General Public License as published by +-# the Free Software Foundation; either version 2 of the License, or +-# (at your option) any later version. +-# +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY; without even the implied warranty of +-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-# GNU General Public License for more details. +-# +-# You should have received a copy of the GNU General Public License +-# along with this program; if not, write to the Free Software +-# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, +-# USA. +- +-import base64 +-import os.path +-import re +-import sys +-import textwrap +-import io +- +-objects = [] +- +-# Dirty file parser. +-in_data, in_multiline, in_obj = False, False, False +-field, type, value, obj = None, None, None, dict() +- +-# Python 3 will not let us decode non-ascii characters if we +-# have not specified an encoding, but Python 2's open does not +-# have an option to set the encoding. Python 3's open is io.open +-# and io.open has been backported to Python 2.6 and 2.7, so use io.open. +-for line in io.open('certdata.txt', 'rt', encoding='utf8'): +- # Ignore the file header. +- if not in_data: +- if line.startswith('BEGINDATA'): +- in_data = True +- continue +- # Ignore comment lines. +- if line.startswith('#'): +- continue +- # Empty lines are significant if we are inside an object. +- if in_obj and len(line.strip()) == 0: +- objects.append(obj) +- obj = dict() +- in_obj = False +- continue +- if len(line.strip()) == 0: +- continue +- if in_multiline: +- if not line.startswith('END'): +- if type == 'MULTILINE_OCTAL': +- line = line.strip() +- for i in re.finditer(r'\\([0-3][0-7][0-7])', line): +- value.append(int(i.group(1), 8)) +- else: +- value += line +- continue +- obj[field] = value +- in_multiline = False +- continue +- if line.startswith('CKA_CLASS'): +- in_obj = True +- line_parts = line.strip().split(' ', 2) +- if len(line_parts) > 2: +- field, type = line_parts[0:2] +- value = ' '.join(line_parts[2:]) +- elif len(line_parts) == 2: +- field, type = line_parts +- value = None +- else: +- raise NotImplementedError('line_parts < 2 not supported.') +- if type == 'MULTILINE_OCTAL': +- in_multiline = True +- value = bytearray() +- continue +- obj[field] = value +-if len(obj) > 0: +- objects.append(obj) +- +-# Read blacklist. +-blacklist = [] +-if os.path.exists('blacklist.txt'): +- for line in open('blacklist.txt', 'r'): +- line = line.strip() +- if line.startswith('#') or len(line) == 0: +- continue +- item = line.split('#', 1)[0].strip() +- blacklist.append(item) +- +-# Build up trust database. +-trust = dict() +-for obj in objects: +- if obj['CKA_CLASS'] != 'CKO_NSS_TRUST': +- continue +- if obj['CKA_LABEL'] in blacklist: +- print("Certificate %s blacklisted, ignoring." % obj['CKA_LABEL']) +- elif obj['CKA_TRUST_SERVER_AUTH'] == 'CKT_NSS_TRUSTED_DELEGATOR': +- trust[obj['CKA_LABEL']] = True +- elif obj['CKA_TRUST_EMAIL_PROTECTION'] == 'CKT_NSS_TRUSTED_DELEGATOR': +- trust[obj['CKA_LABEL']] = True +- elif obj['CKA_TRUST_SERVER_AUTH'] == 'CKT_NSS_NOT_TRUSTED': +- print('!'*74) +- print("UNTRUSTED BUT NOT BLACKLISTED CERTIFICATE FOUND: %s" % obj['CKA_LABEL']) +- print('!'*74) +- else: +- print("Ignoring certificate %s. SAUTH=%s, EPROT=%s" % \ +- (obj['CKA_LABEL'], obj['CKA_TRUST_SERVER_AUTH'], +- obj['CKA_TRUST_EMAIL_PROTECTION'])) +- +-for obj in objects: +- if obj['CKA_CLASS'] == 'CKO_CERTIFICATE': +- if not obj['CKA_LABEL'] in trust or not trust[obj['CKA_LABEL']]: +- continue +- bname = obj['CKA_LABEL'][1:-1].replace('/', '_')\ +- .replace(' ', '_')\ +- .replace('(', '=')\ +- .replace(')', '=')\ +- .replace(',', '_') +- +- # this is the only way to decode the way NSS stores multi-byte UTF-8 +- # and we need an escaped string for checking existence of things +- # otherwise we're dependant on the user's current locale. +- if bytes != str: +- # We're in python 3, convert the utf-8 string to a +- # sequence of bytes that represents this utf-8 string +- # then encode the byte-sequence as an escaped string that +- # can be passed to open() and os.path.exists() +- bname = bname.encode('utf-8').decode('unicode_escape').encode('latin-1') +- else: +- # Python 2 +- # Convert the unicode string back to its original byte form +- # (contents of files returned by io.open are returned as +- # unicode strings) +- # then to an escaped string that can be passed to open() +- # and os.path.exists() +- bname = bname.encode('utf-8').decode('string_escape') +- +- fname = bname + b'.crt' +- if os.path.exists(fname): +- print("Found duplicate certificate name %s, renaming." % bname) +- fname = bname + b'_2.crt' +- f = open(fname, 'w') +- f.write("-----BEGIN CERTIFICATE-----\n") +- encoded = base64.b64encode(obj['CKA_VALUE']).decode('utf-8') +- f.write("\n".join(textwrap.wrap(encoded, 64))) +- f.write("\n-----END CERTIFICATE-----\n") +- +diff --git a/mk-ca-bundle.pl b/mk-ca-bundle.pl +new file mode 100644 +index 0000000..09e8e5b +--- /dev/null ++++ b/mk-ca-bundle.pl +@@ -0,0 +1,604 @@ ++#!/usr/bin/env perl ++# *************************************************************************** ++# * _ _ ____ _ ++# * Project ___| | | | _ \| | ++# * / __| | | | |_) | | ++# * | (__| |_| | _ <| |___ ++# * \___|\___/|_| \_\_____| ++# * ++# * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, et al. ++# * ++# * This software is licensed as described in the file COPYING, which ++# * you should have received as part of this distribution. The terms ++# * are also available at https://curl.haxx.se/docs/copyright.html. ++# * ++# * You may opt to use, copy, modify, merge, publish, distribute and/or sell ++# * copies of the Software, and permit persons to whom the Software is ++# * furnished to do so, under the terms of the COPYING file. ++# * ++# * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY ++# * KIND, either express or implied. ++# * ++# *************************************************************************** ++# This Perl script creates a fresh ca-bundle.crt file for use with libcurl. ++# It downloads certdata.txt from Mozilla's source tree (see URL below), ++# then parses certdata.txt and extracts CA Root Certificates into PEM format. ++# These are then processed with the OpenSSL commandline tool to produce the ++# final ca-bundle.crt file. ++# The script is based on the parse-certs script written by Roland Krikava. ++# This Perl script works on almost any platform since its only external ++# dependency is the OpenSSL commandline tool for optional text listing. ++# Hacked by Guenter Knauf. ++# ++use Encode; ++use Getopt::Std; ++use MIME::Base64; ++use strict; ++use warnings; ++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; ++use Time::Local; ++my $MOD_SHA = "Digest::SHA"; ++eval "require $MOD_SHA"; ++if ($@) { ++ $MOD_SHA = "Digest::SHA::PurePerl"; ++ eval "require $MOD_SHA"; ++} ++eval "require LWP::UserAgent"; ++ ++my %urls = ( ++ 'nss' => ++ 'https://hg.mozilla.org/projects/nss/raw-file/default/lib/ckfw/builtins/certdata.txt', ++ 'central' => ++ 'https://hg.mozilla.org/mozilla-central/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt', ++ 'beta' => ++ 'https://hg.mozilla.org/releases/mozilla-beta/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt', ++ 'release' => ++ 'https://hg.mozilla.org/releases/mozilla-release/raw-file/default/security/nss/lib/ckfw/builtins/certdata.txt', ++); ++ ++$opt_d = 'release'; ++ ++# If the OpenSSL commandline is not in search path you can configure it here! ++my $openssl = 'openssl'; ++ ++my $version = '1.27'; ++ ++$opt_w = 76; # default base64 encoded lines length ++ ++# default cert types to include in the output (default is to include CAs which may issue SSL server certs) ++my $default_mozilla_trust_purposes = "SERVER_AUTH"; ++my $default_mozilla_trust_levels = "TRUSTED_DELEGATOR"; ++$opt_p = $default_mozilla_trust_purposes . ":" . $default_mozilla_trust_levels; ++ ++my @valid_mozilla_trust_purposes = ( ++ "DIGITAL_SIGNATURE", ++ "NON_REPUDIATION", ++ "KEY_ENCIPHERMENT", ++ "DATA_ENCIPHERMENT", ++ "KEY_AGREEMENT", ++ "KEY_CERT_SIGN", ++ "CRL_SIGN", ++ "SERVER_AUTH", ++ "CLIENT_AUTH", ++ "CODE_SIGNING", ++ "EMAIL_PROTECTION", ++ "IPSEC_END_SYSTEM", ++ "IPSEC_TUNNEL", ++ "IPSEC_USER", ++ "TIME_STAMPING", ++ "STEP_UP_APPROVED" ++); ++ ++my @valid_mozilla_trust_levels = ( ++ "TRUSTED_DELEGATOR", # CAs ++ "NOT_TRUSTED", # Don't trust these certs. ++ "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. ++ "TRUSTED" # This cert is trusted, but only for itself and not for delegates (i.e. it is not a CA). ++); ++ ++my $default_signature_algorithms = $opt_s = "MD5"; ++ ++my @valid_signature_algorithms = ( ++ "MD5", ++ "SHA1", ++ "SHA256", ++ "SHA384", ++ "SHA512" ++); ++ ++$0 =~ s@.*(/|\\)@@; ++$Getopt::Std::STANDARD_HELP_VERSION = 1; ++getopts('bd:fhiklmnp:qs:tuvw:'); ++ ++if(!defined($opt_d)) { ++ # to make plain "-d" use not cause warnings, and actually still work ++ $opt_d = 'release'; ++} ++ ++# Use predefined URL or else custom URL specified on command line. ++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`; ++ ++if ($opt_i) { ++ print ("=" x 78 . "\n"); ++ print "Script Version : $version\n"; ++ print "Perl Version : $]\n"; ++ print "Operating System Name : $^O\n"; ++ print "Getopt::Std.pm Version : ${Getopt::Std::VERSION}\n"; ++ print "Encode::Encoding.pm Version : ${Encode::Encoding::VERSION}\n"; ++ print "MIME::Base64.pm Version : ${MIME::Base64::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"); ++} ++ ++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) 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"; ++ print " 5) Mozilla apps use additional security checks aren't represented in certdata\n"; ++ print " 6) Use of this script will make a security engineer grind his teeth and\n"; ++ print " swear at you. ;)\n"; ++ exit; ++ } else { # Short Form Warning ++ print "Warning: Use of this script may pose some risk, -d risk for more details.\n"; ++ } ++} ++ ++sub HELP_MESSAGE() { ++ 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"; ++ 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"; ++ print "\t\t Valid purposes are:\n"; ++ print wrap("\t\t ","\t\t ", join( ", ", "ALL", @valid_mozilla_trust_purposes ) ), "\n"; ++ print "\t\t Valid levels are:\n"; ++ print wrap("\t\t ","\t\t ", join( ", ", "ALL", @valid_mozilla_trust_levels ) ), "\n"; ++ print "\t-q\tbe really quiet (no progress output at all)\n"; ++ print wrap("\t","\t\t", "-s\tcomma separated list of certificate signatures/hashes to output in plain text mode. (default: $default_signature_algorithms)\n"); ++ print "\t\t Valid signature algorithms are:\n"; ++ print wrap("\t\t ","\t\t ", join( ", ", "ALL", @valid_signature_algorithms ) ), "\n"; ++ print "\t-t\tinclude plain text listing of certificates\n"; ++ print "\t-u\tunlink (remove) certdata.txt after processing\n"; ++ print "\t-v\tbe verbose and print out processed CAs\n"; ++ print "\t-w <l>\twrap base64 output lines after <l> chars (default: ${opt_w})\n"; ++ exit; ++} ++ ++sub VERSION_MESSAGE() { ++ print "${0} version ${version} running Perl ${]} on ${^O}\n"; ++} ++ ++warning_message() unless ($opt_q || $url =~ m/^(ht|f)tps:/i ); ++HELP_MESSAGE() if ($opt_h); ++ ++sub report($@) { ++ my $output = shift; ++ ++ print STDERR $output . "\n" unless $opt_q; ++} ++ ++sub is_in_list($@) { ++ my $target = shift; ++ ++ return defined(List::Util::first { $target eq $_ } @_); ++} ++ ++# Parses $param_string as a case insensitive comma separated list with optional whitespace ++# validates that only allowed parameters are supplied ++sub parse_csv_param($$@) { ++ my $description = shift; ++ my $param_string = shift; ++ my @valid_values = @_; ++ ++ my @values = map { ++ s/^\s+//; # strip leading spaces ++ s/\s+$//; # strip trailing spaces ++ uc $_ # return the modified string as upper case ++ } split( ',', $param_string ); ++ ++ # Find all values which are not in the list of valid values or "ALL" ++ my @invalid = grep { !is_in_list($_,"ALL",@valid_values) } @values; ++ ++ if ( scalar(@invalid) > 0 ) { ++ # Tell the user which parameters were invalid and print the standard help message which will exit ++ print "Error: Invalid ", $description, scalar(@invalid) == 1 ? ": " : "s: ", join( ", ", map { "\"$_\"" } @invalid ), "\n"; ++ HELP_MESSAGE(); ++ } ++ ++ @values = @valid_values if ( is_in_list("ALL",@values) ); ++ ++ return @values; ++} ++ ++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(256)->addfile(*FILE)->hexdigest; ++ close(FILE); ++ } else { ++ # Use OpenSSL command if Perl Digest::SHA modules not available ++ $result = `"$openssl" dgst -r -sha256 "$_[0]"`; ++ $result =~ s/^([0-9a-f]{64}) .+/$1/is; ++ } ++ return $result; ++} ++ ++ ++sub oldhash { ++ my $hash = ""; ++ open(C, "<$_[0]") || return 0; ++ while(<C>) { ++ chomp; ++ if($_ =~ /^\#\# SHA256: (.*)/) { ++ $hash = $1; ++ last; ++ } ++ } ++ close(C); ++ return $hash; ++} ++ ++if ( $opt_p !~ m/:/ ) { ++ print "Error: Mozilla trust identifier list must include both purposes and levels\n"; ++ HELP_MESSAGE(); ++} ++ ++(my $included_mozilla_trust_purposes_string, my $included_mozilla_trust_levels_string) = split( ':', $opt_p ); ++my @included_mozilla_trust_purposes = parse_csv_param( "trust purpose", $included_mozilla_trust_purposes_string, @valid_mozilla_trust_purposes ); ++my @included_mozilla_trust_levels = parse_csv_param( "trust level", $included_mozilla_trust_levels_string, @valid_mozilla_trust_levels ); ++ ++my @included_signature_algorithms = parse_csv_param( "signature algorithm", $opt_s, @valid_signature_algorithms ); ++ ++sub should_output_cert(%) { ++ my %trust_purposes_by_level = @_; ++ ++ foreach my $level (@included_mozilla_trust_levels) { ++ # for each level we want to output, see if any of our desired purposes are included ++ return 1 if ( defined( List::Util::first { is_in_list( $_, @included_mozilla_trust_purposes ) } @{$trust_purposes_by_level{$level}} ) ); ++ } ++ ++ return 0; ++} ++ ++my $crt = $ARGV[0] || 'ca-bundle.crt'; ++(my $txt = $url) =~ s@(.*/|\?.*)@@g; ++ ++my $stdout = $crt eq '-'; ++my $resp; ++my $fetched; ++ ++my $oldhash = oldhash($crt); ++ ++report "SHA256 of old file: $oldhash"; ++ ++if(!$opt_n) { ++ report "Downloading $txt ..."; ++ ++ # 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"; ++ } ++ } ++ ++ # 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; ++ 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; ++ } ++ } ++} ++ ++my $filedate = $resp ? $resp->last_modified : (stat($txt))[9]; ++my $datesrc = "as of"; ++if(!$filedate) { ++ # mxr.mozilla.org gave us a time, hg.mozilla.org does not! ++ $filedate = time(); ++ $datesrc="downloaded on"; ++} ++ ++# get the hash from the download file ++my $newhash= sha256($txt); ++ ++if(!$opt_f && $oldhash eq $newhash) { ++ report "Downloaded file identical to previous run\'s source file. Exiting"; ++ if($opt_u && -e $txt && !unlink($txt)) { ++ report "Failed to remove $txt: $!\n"; ++ } ++ exit; ++} ++ ++report "SHA256 of new file: $newhash"; ++ ++my $currentdate = scalar gmtime($filedate); ++ ++my $format = $opt_t ? "plain text and " : ""; ++if( $stdout ) { ++ open(CRT, '> -') or die "Couldn't open STDOUT: $!\n"; ++} else { ++ open(CRT,">$crt.~") or die "Couldn't open $crt.~: $!\n"; ++} ++print CRT <<EOT; ++## ++## Bundle of CA Root Certificates ++## ++## 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 ++## file (certdata.txt). This file can be found in the mozilla source tree: ++## ${url} ++## ++## It contains the certificates in ${format}PEM format and therefore ++## can be directly used with curl / libcurl / php_curl, or with ++## an Apache+mod_ssl webserver for SSL client authentication. ++## Just configure this file as the SSLCACertificateFile. ++## ++## Conversion done with mk-ca-bundle.pl version $version. ++## SHA256: $newhash ++## ++ ++EOT ++ ++report "Processing '$txt' ..."; ++my $caname; ++my $certnum = 0; ++my $skipnum = 0; ++my $start_of_cert = 0; ++my @precert; ++my $cka_value; ++my $valid = 1; ++ ++open(TXT,"$txt") or die "Couldn't open $txt: $!\n"; ++while (<TXT>) { ++ if (/\*\*\*\*\* BEGIN LICENSE BLOCK \*\*\*\*\*/) { ++ print CRT; ++ print if ($opt_l); ++ while (<TXT>) { ++ print CRT; ++ print if ($opt_l); ++ last if (/\*\*\*\*\* END LICENSE BLOCK \*\*\*\*\*/); ++ } ++ } ++ elsif(/^# (Issuer|Serial Number|Subject|Not Valid Before|Not Valid After |Fingerprint \(MD5\)|Fingerprint \(SHA1\)):/) { ++ push @precert, $_; ++ $valid = 1; ++ next; ++ } ++ elsif(/^#|^\s*$/) { ++ undef @precert; ++ next; ++ } ++ chomp; ++ ++ # Example: ++ # CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL ++ # \062\060\060\066\061\067\060\060\060\060\060\060\132 ++ # END ++ ++ if (/^CKA_NSS_SERVER_DISTRUST_AFTER (CK_BBOOL CK_FALSE|MULTILINE_OCTAL)/) { ++ if($1 eq "MULTILINE_OCTAL") { ++ my @timestamp; ++ while (<TXT>) { ++ last if (/^END/); ++ chomp; ++ my @octets = split(/\\/); ++ shift @octets; ++ for (@octets) { ++ push @timestamp, chr(oct); ++ } ++ } ++ # A trailing Z in the timestamp signifies UTC ++ if($timestamp[12] ne "Z") { ++ report "distrust date stamp is not using UTC"; ++ } ++ # Example date: 200617000000Z ++ # Means 2020-06-17 00:00:00 UTC ++ my $distrustat = ++ timegm($timestamp[10] . $timestamp[11], # second ++ $timestamp[8] . $timestamp[9], # minute ++ $timestamp[6] . $timestamp[7], # hour ++ $timestamp[4] . $timestamp[5], # day ++ ($timestamp[2] . $timestamp[3]) - 1, # month ++ "20" . $timestamp[0] . $timestamp[1]); # year ++ if(time >= $distrustat) { ++ # not trusted anymore ++ $skipnum++; ++ report "Skipping: $caname is not trusted anymore" if ($opt_v); ++ $valid = 0; ++ } ++ else { ++ # still trusted ++ } ++ } ++ next; ++ } ++ ++ # this is a match for the start of a certificate ++ if (/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/) { ++ $start_of_cert = 1 ++ } ++ if ($start_of_cert && /^CKA_LABEL UTF8 \"(.*)\"/) { ++ $caname = $1; ++ } ++ my %trust_purposes_by_level; ++ if ($start_of_cert && /^CKA_VALUE MULTILINE_OCTAL/) { ++ $cka_value=""; ++ while (<TXT>) { ++ last if (/^END/); ++ chomp; ++ my @octets = split(/\\/); ++ shift @octets; ++ for (@octets) { ++ $cka_value .= chr(oct); ++ } ++ } ++ } ++ if(/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/ && $valid) { ++ # now scan the trust part to determine how we should trust this cert ++ while (<TXT>) { ++ last if (/^#/); ++ if (/^CKA_TRUST_([A-Z_]+)\s+CK_TRUST\s+CKT_NSS_([A-Z_]+)\s*$/) { ++ if ( !is_in_list($1,@valid_mozilla_trust_purposes) ) { ++ report "Warning: Unrecognized trust purpose for cert: $caname. Trust purpose: $1. Trust Level: $2"; ++ } elsif ( !is_in_list($2,@valid_mozilla_trust_levels) ) { ++ report "Warning: Unrecognized trust level for cert: $caname. Trust purpose: $1. Trust Level: $2"; ++ } else { ++ push @{$trust_purposes_by_level{$2}}, $1; ++ } ++ } ++ } ++ ++ if ( !should_output_cert(%trust_purposes_by_level) ) { ++ $skipnum ++; ++ report "Skipping: $caname" if ($opt_v); ++ } else { ++ my $data = $cka_value; ++ $cka_value = ""; ++ my $encoded = MIME::Base64::encode_base64($data, ''); ++ $encoded =~ s/(.{1,${opt_w}})/$1\n/g; ++ my $pem = "-----BEGIN CERTIFICATE-----\n" ++ . $encoded ++ . "-----END CERTIFICATE-----\n"; ++ print CRT "\n$caname\n"; ++ print CRT @precert if($opt_m); ++ my $maxStringLength = length(decode('UTF-8', $caname, Encode::FB_CROAK | Encode::LEAVE_SRC)); ++ if ($opt_t) { ++ foreach my $key (keys %trust_purposes_by_level) { ++ my $string = $key . ": " . join(", ", @{$trust_purposes_by_level{$key}}); ++ $maxStringLength = List::Util::max( length($string), $maxStringLength ); ++ print CRT $string . "\n"; ++ } ++ } ++ print CRT ("=" x $maxStringLength . "\n"); ++ if (!$opt_t) { ++ print CRT $pem; ++ } else { ++ my $pipe = ""; ++ foreach my $hash (@included_signature_algorithms) { ++ $pipe = "|$openssl x509 -" . $hash . " -fingerprint -noout -inform PEM"; ++ if (!$stdout) { ++ $pipe .= " >> $crt.~"; ++ close(CRT) or die "Couldn't close $crt.~: $!"; ++ } ++ open(TMP, $pipe) or die "Couldn't open openssl pipe: $!"; ++ print TMP $pem; ++ close(TMP) or die "Couldn't close openssl pipe: $!"; ++ if (!$stdout) { ++ open(CRT, ">>$crt.~") or die "Couldn't open $crt.~: $!"; ++ } ++ } ++ $pipe = "|$openssl x509 -text -inform PEM"; ++ if (!$stdout) { ++ $pipe .= " >> $crt.~"; ++ close(CRT) or die "Couldn't close $crt.~: $!"; ++ } ++ open(TMP, $pipe) or die "Couldn't open openssl pipe: $!"; ++ print TMP $pem; ++ close(TMP) or die "Couldn't close openssl pipe: $!"; ++ if (!$stdout) { ++ open(CRT, ">>$crt.~") or die "Couldn't open $crt.~: $!"; ++ } ++ } ++ report "Parsing: $caname" if ($opt_v); ++ $certnum ++; ++ $start_of_cert = 0; ++ } ++ undef @precert; ++ } ++ ++} ++close(TXT) or die "Couldn't close $txt: $!\n"; ++close(CRT) or die "Couldn't close $crt.~: $!\n"; ++unless( $stdout ) { ++ if ($opt_b && -e $crt) { ++ my $bk = 1; ++ while (-e "$crt.~${bk}~") { ++ $bk++; ++ } ++ rename $crt, "$crt.~${bk}~" or die "Failed to create backup $crt.~$bk}~: $!\n"; ++ } elsif( -e $crt ) { ++ unlink( $crt ) or die "Failed to remove $crt: $!\n"; ++ } ++ rename "$crt.~", $crt or die "Failed to rename $crt.~ to $crt: $!\n"; ++} ++if($opt_u && -e $txt && !unlink($txt)) { ++ report "Failed to remove $txt: $!\n"; ++} ++report "Done ($certnum CA certs processed, $skipnum skipped)."; +diff --git a/split-ca-bundle.sh b/split-ca-bundle.sh +new file mode 100644 +index 0000000..d0f39a8 +--- /dev/null ++++ b/split-ca-bundle.sh +@@ -0,0 +1,30 @@ ++#!/bin/sh ++ ++mkcert() { ++ local name="$1" ++ local line ++ rm -f "$name" ++ while read line; do ++ printf "%s\n" "$line" >> "$name" ++ if [ "$line" = "-----END CERTIFICATE-----" ]; then ++ break; ++ fi ++ done ++} ++ ++prev= ++while read line; do ++ case "$line" in ++ =*=) ++ fname="$(printf "%s" "$prev" | tr '/ (),' '__==_').crt" ++ while read cline; do ++ printf "%s\n" "$cline" ++ if [ "$cline" = "-----END CERTIFICATE-----" ]; then ++ break; ++ fi ++ done > "$fname" ++ ;; ++ esac ++ prev="$line" ++done ++ +-- +2.25.0 + diff --git a/main/ca-certificates/APKBUILD b/main/ca-certificates/APKBUILD index d1a77a424f..81ecb9b6e2 100644 --- a/main/ca-certificates/APKBUILD +++ b/main/ca-certificates/APKBUILD @@ -2,42 +2,46 @@ # Maintainer: Natanael Copa <ncopa@alpinelinux.org> pkgname=ca-certificates pkgver=20191127 -pkgrel=1 -pkgdesc="Common CA certificates PEM files" +pkgrel=2 +pkgdesc="Common CA certificates PEM files from Mozilla" url="https://www.mozilla.org/en-US/about/governance/policies/security-group/certs/" arch="all" license="MPL-2.0 GPL-2.0-or-later" -depends="" -makedepends_build="python3" +makedepends_build="perl" makedepends_host="openssl-dev" -subpackages="$pkgname-doc $pkgname-cacert" +subpackages="$pkgname-doc $pkgname-bundle" # c_rehash is either in libcrypto1.0 or openssl depending on package, grr. replace both of them replaces="libcrypto1.0 openssl openssl1.0" options="!fhs !check" triggers="ca-certificates.trigger=/usr/share/ca-certificates:/usr/local/share/ca-certificates:/etc/ssl/certs:/etc/ca-certificates/update.d" install="$pkgname.post-deinstall" source="https://git.alpinelinux.org/ca-certificates/snapshot/ca-certificates-$pkgver.tar.xz + 0001-update-ca-fix-compiler-warning.patch + 0002-replace-python-script-with-perl-script.patch 0003-update-ca-insert-newline-between-certs.patch " -builddir="$srcdir/ca-certificates-$pkgver" build() { - cd "$builddir" make } package() { - cd "$builddir" make install DESTDIR="$pkgdir" ( - echo "# Automatically generated by ${pkgname}-${pkgver}-${pkgrel}" + echo "# Automatically generated by $pkgname-$pkgver-$pkgrel" echo "# $(date -u)" echo "# Do not edit." cd "$pkgdir"/usr/share/ca-certificates find . -name '*.crt' | sort | cut -b3- ) > "$pkgdir"/etc/ca-certificates.conf + # generate the bundle in similar way as update-ca-certificates would do + for i in $(ls *.crt | sort); do + cat "$i" + printf "\n" + done > "$pkgdir"/etc/ssl/certs/ca-certificates.crt + mkdir -p "$pkgdir"/etc/apk/protected_paths.d cat > "$pkgdir"/etc/apk/protected_paths.d/ca-certificates.list <<-EOF -etc/ssl/certs/ca-certificates.crt @@ -52,13 +56,18 @@ package() { chmod +x "$pkgdir"/etc/ca-certificates/update.d/certhash } -cacert() { - pkgdesc="Mozilla bundled certificates" +bundle() { + pkgdesc="Pre generated bundle of Mozilla certificates" replaces="libressl2.7-libcrypto" - mkdir -p "$subpkgdir"/etc/ssl - cat "$pkgdir"/usr/share/ca-certificates/mozilla/*.crt > \ + provides="$pkgname-cacert=$pkgver-r$pkgrel" + mkdir -p "$subpkgdir"/etc/ssl/certs + mv "$pkgdir"/etc/ssl/certs/ca-certificates.crt \ + "$subpkgdir"/etc/ssl/certs/ + ln -s certs/ca-certificates.crt \ "$subpkgdir"/etc/ssl/cert.pem } sha512sums="68a879680a5e20764b8a4ee3019e9a008193c578a687b0d29694355a679c04cbfa94d4049beb3c52a899d593f46254c94d67db833f39e91325a4476963b9ef18 ca-certificates-20191127.tar.xz +aafe6d9047380fc403792fbf27146dc9c0532ef401e6eb9bd8b533c110f902cad0a66701cf3563ad625d07ae54619e9f2f3091ec14772b92e178dbed142ecd97 0001-update-ca-fix-compiler-warning.patch +4d9c71b9ea0596f5efaa188f244b7ab587f96c218bb6fed01f11e34c553909f65bbe660156f8300be9511ae50614661c5dcd3b493ac146a8e888f62fc52bd9d4 0002-replace-python-script-with-perl-script.patch 051b5d78916ee7389dfbd4e8871aab720415bd6e9ee0313dba770fc40ee7c68ac67d7918f2503458a3218e3bfc10691b5e379b65269106fde02c7e7a36eb7595 0003-update-ca-insert-newline-between-certs.patch" |