[1/3] Initial infrastructure for SSL root bundle in base
This setup will add the trusted certificates from the Mozilla NSS bundle to base. This commit includes: - CAROOT option to opt out of installation of certs - mtree amendments for final destinations - infrastructure to fetch/update certs, along with instructions A follow-up commit will add a certctl(8) utility to give the user control over trust specifics. Another follow-up commit will actually commit the initial result of updatecerts. This work was done primarily by allanjude@, with minor contributions by myself. No objection from: secteam Relnotes: yes Differential Revision: https://reviews.freebsd.org/D16856
This commit is contained in:
parent
dceda44f26
commit
4ed49b4dcb
@ -194,6 +194,12 @@
|
||||
uk_UA.KOI8-U
|
||||
..
|
||||
..
|
||||
certs
|
||||
blacklisted
|
||||
..
|
||||
trusted
|
||||
..
|
||||
..
|
||||
dict
|
||||
..
|
||||
doc
|
||||
|
@ -8,6 +8,8 @@ SUBDIR_PARALLEL=
|
||||
|
||||
SUBDIR.${MK_TESTS}+= tests
|
||||
|
||||
SUBDIR.${MK_CAROOT}+= caroot
|
||||
|
||||
# These are the programs which depend on crypto, but not Kerberos.
|
||||
SPROGS= lib/libfetch lib/libpam lib/libradius lib/libtelnet \
|
||||
bin/ed libexec/telnetd usr.bin/fetch usr.bin/telnet \
|
||||
|
272
secure/caroot/MAca-bundle.pl
Executable file
272
secure/caroot/MAca-bundle.pl
Executable file
@ -0,0 +1,272 @@
|
||||
#!/usr/bin/env perl
|
||||
##
|
||||
## MAca-bundle.pl -- Regenerate ca-root-nss.crt from the Mozilla certdata.txt
|
||||
##
|
||||
## Rewritten in September 2011 by Matthias Andree to heed untrust
|
||||
##
|
||||
|
||||
## Copyright (c) 2011, 2013 Matthias Andree <mandree@FreeBSD.org>
|
||||
## All rights reserved.
|
||||
## Copyright (c) 2018, Allan Jude <allanjude@FreeBSD.org>
|
||||
##
|
||||
## Redistribution and use in source and binary forms, with or without
|
||||
## modification, are permitted provided that the following conditions are
|
||||
## met:
|
||||
##
|
||||
## * Redistributions of source code must retain the above copyright
|
||||
## notice, this list of conditions and the following disclaimer.
|
||||
##
|
||||
## * Redistributions in binary form must reproduce the above copyright
|
||||
## notice, this list of conditions and the following disclaimer in the
|
||||
## documentation and/or other materials provided with the distribution.
|
||||
##
|
||||
## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
||||
## FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
||||
## COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
||||
## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
## LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
## CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
## LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
## ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
## POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use strict;
|
||||
use Carp;
|
||||
use MIME::Base64;
|
||||
use Getopt::Long;
|
||||
|
||||
my $VERSION = '$FreeBSD$';
|
||||
my $inputfh = *STDIN;
|
||||
my $debug = 0;
|
||||
my $infile;
|
||||
my $outputdir;
|
||||
my %labels;
|
||||
my %certs;
|
||||
my %trusts;
|
||||
|
||||
$debug++
|
||||
if defined $ENV{'WITH_DEBUG'}
|
||||
and $ENV{'WITH_DEBUG'} !~ m/(?i)^(no|0|false|)$/;
|
||||
|
||||
GetOptions (
|
||||
"debug+" => \$debug,
|
||||
"infile:s" => \$infile,
|
||||
"outputdir:s" => \$outputdir)
|
||||
or die("Error in command line arguments\n$0 [-d] [-i input-file] [-o output-dir]\n");
|
||||
|
||||
if ($infile) {
|
||||
open($inputfh, "<", $infile) or die "Failed to open $infile";
|
||||
}
|
||||
|
||||
sub print_header($$)
|
||||
{
|
||||
my $dstfile = shift;
|
||||
my $label = shift;
|
||||
|
||||
if ($outputdir) {
|
||||
print $dstfile <<EOFH;
|
||||
##
|
||||
## $label
|
||||
##
|
||||
## This is a single X.509 certificate for a public Certificate
|
||||
## Authority (CA). It was automatically extracted from Mozilla's
|
||||
## root CA list (the file `certdata.txt' in security/nss).
|
||||
##
|
||||
## Extracted from nss
|
||||
## with $VERSION
|
||||
##
|
||||
EOFH
|
||||
} else {
|
||||
print $dstfile <<EOH;
|
||||
##
|
||||
## ca-root-nss.crt -- Bundle of CA Root Certificates
|
||||
##
|
||||
## This is a bundle of X.509 certificates of public Certificate
|
||||
## Authorities (CA). These were automatically extracted from Mozilla's
|
||||
## root CA list (the file `certdata.txt').
|
||||
##
|
||||
## Extracted from nss
|
||||
## with $VERSION
|
||||
##
|
||||
EOH
|
||||
}
|
||||
}
|
||||
|
||||
sub printcert($$$)
|
||||
{
|
||||
my ($fh, $label, $certdata) = @_;
|
||||
return unless $certdata;
|
||||
open(OUT, "|openssl x509 -text -inform DER -fingerprint")
|
||||
or die "could not pipe to openssl x509";
|
||||
print OUT $certdata;
|
||||
close(OUT) or die "openssl x509 failed with exit code $?";
|
||||
}
|
||||
|
||||
sub graboct($)
|
||||
{
|
||||
my $ifh = shift;
|
||||
my $data;
|
||||
|
||||
while (<$ifh>) {
|
||||
last if /^END/;
|
||||
my (undef,@oct) = split /\\/;
|
||||
my @bin = map(chr(oct), @oct);
|
||||
$data .= join('', @bin);
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
|
||||
sub grabcert($)
|
||||
{
|
||||
my $ifh = shift;
|
||||
my $certdata;
|
||||
my $cka_label;
|
||||
my $serial;
|
||||
|
||||
while (<$ifh>) {
|
||||
chomp;
|
||||
last if ($_ eq '');
|
||||
|
||||
if (/^CKA_LABEL UTF8 "([^"]+)"/) {
|
||||
$cka_label = $1;
|
||||
}
|
||||
|
||||
if (/^CKA_VALUE MULTILINE_OCTAL/) {
|
||||
$certdata = graboct($ifh);
|
||||
}
|
||||
|
||||
if (/^CKA_SERIAL_NUMBER MULTILINE_OCTAL/) {
|
||||
$serial = graboct($ifh);
|
||||
}
|
||||
}
|
||||
return ($serial, $cka_label, $certdata);
|
||||
}
|
||||
|
||||
sub grabtrust($) {
|
||||
my $ifh = shift;
|
||||
my $cka_label;
|
||||
my $serial;
|
||||
my $maytrust = 0;
|
||||
my $distrust = 0;
|
||||
|
||||
while (<$ifh>) {
|
||||
chomp;
|
||||
last if ($_ eq '');
|
||||
|
||||
if (/^CKA_LABEL UTF8 "([^"]+)"/) {
|
||||
$cka_label = $1;
|
||||
}
|
||||
|
||||
if (/^CKA_SERIAL_NUMBER MULTILINE_OCTAL/) {
|
||||
$serial = graboct($ifh);
|
||||
}
|
||||
|
||||
if (/^CKA_TRUST_(SERVER_AUTH|EMAIL_PROTECTION|CODE_SIGNING) CK_TRUST (\S+)$/)
|
||||
{
|
||||
if ($2 eq 'CKT_NSS_NOT_TRUSTED') {
|
||||
$distrust = 1;
|
||||
} elsif ($2 eq 'CKT_NSS_TRUSTED_DELEGATOR') {
|
||||
$maytrust = 1;
|
||||
} elsif ($2 ne 'CKT_NSS_MUST_VERIFY_TRUST') {
|
||||
confess "Unknown trust setting on line $.:\n"
|
||||
. "$_\n"
|
||||
. "Script must be updated:";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$maytrust && !$distrust && $debug) {
|
||||
print STDERR "line $.: no explicit trust/distrust found for $cka_label\n";
|
||||
}
|
||||
|
||||
my $trust = ($maytrust and not $distrust);
|
||||
return ($serial, $cka_label, $trust);
|
||||
}
|
||||
|
||||
if (!$outputdir) {
|
||||
print_header(*STDOUT, "");
|
||||
}
|
||||
|
||||
while (<$inputfh>) {
|
||||
if (/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/) {
|
||||
my ($serial, $label, $certdata) = grabcert($inputfh);
|
||||
if (defined $certs{$label."\0".$serial}) {
|
||||
warn "Certificate $label duplicated!\n";
|
||||
}
|
||||
$certs{$label."\0".$serial} = $certdata;
|
||||
# We store the label in a separate hash because truncating the key
|
||||
# with \0 was causing garbage data after the end of the text.
|
||||
$labels{$label."\0".$serial} = $label;
|
||||
} elsif (/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/) {
|
||||
my ($serial, $label, $trust) = grabtrust($inputfh);
|
||||
if (defined $trusts{$label."\0".$serial}) {
|
||||
warn "Trust for $label duplicated!\n";
|
||||
}
|
||||
$trusts{$label."\0".$serial} = $trust;
|
||||
$labels{$label."\0".$serial} = $label;
|
||||
} elsif (/^CVS_ID.*Revision: ([^ ]*).*/) {
|
||||
print "## Source: \"certdata.txt\" CVS revision $1\n##\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub label_to_filename(@) {
|
||||
my @res = @_;
|
||||
map { s/\0.*//; s/[^[:alnum:]\-]/_/g; $_ = "$_.pem"; } @res;
|
||||
return wantarray ? @res : $res[0];
|
||||
}
|
||||
|
||||
# weed out untrusted certificates
|
||||
my $untrusted = 0;
|
||||
foreach my $it (keys %trusts) {
|
||||
if (!$trusts{$it}) {
|
||||
if (!exists($certs{$it})) {
|
||||
warn "Found trust for nonexistent certificate $labels{$it}\n" if $debug;
|
||||
} else {
|
||||
delete $certs{$it};
|
||||
warn "Skipping untrusted $labels{$it}\n" if $debug;
|
||||
$untrusted++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$outputdir) {
|
||||
print "## Untrusted certificates omitted from this bundle: $untrusted\n\n";
|
||||
}
|
||||
print STDERR "## Untrusted certificates omitted from this bundle: $untrusted\n";
|
||||
|
||||
my $certcount = 0;
|
||||
foreach my $it (sort {uc($a) cmp uc($b)} keys %certs) {
|
||||
my $fh = *STDOUT;
|
||||
my $filename;
|
||||
if (!exists($trusts{$it})) {
|
||||
die "Found certificate without trust block,\naborting";
|
||||
}
|
||||
if ($outputdir) {
|
||||
$filename = label_to_filename($labels{$it});
|
||||
open($fh, ">", "$outputdir/$filename") or die "Failed to open certificate $filename";
|
||||
print_header($fh, $labels{$it});
|
||||
}
|
||||
printcert($fh, $labels{$it}, $certs{$it});
|
||||
if ($outputdir) {
|
||||
close($fh) or die "Unable to close: $filename";
|
||||
} else {
|
||||
print $fh "\n\n\n";
|
||||
}
|
||||
$certcount++;
|
||||
print STDERR "Trusting $certcount: $labels{$it}\n" if $debug;
|
||||
}
|
||||
|
||||
if ($certcount < 25) {
|
||||
die "Certificate count of $certcount is implausibly low.\nAbort";
|
||||
}
|
||||
|
||||
if (!$outputdir) {
|
||||
print "## Number of certificates: $certcount\n";
|
||||
print "## End of file.\n";
|
||||
}
|
||||
print STDERR "## Number of certificates: $certcount\n";
|
21
secure/caroot/Makefile
Normal file
21
secure/caroot/Makefile
Normal file
@ -0,0 +1,21 @@
|
||||
# $FreeBSD$
|
||||
|
||||
PACKAGE= caroot
|
||||
|
||||
CLEANFILES+= certdata.txt
|
||||
|
||||
SUBDIR+= trusted
|
||||
SUBDIR+= blacklisted
|
||||
|
||||
.include <bsd.prog.mk>
|
||||
|
||||
# To be used by secteam@ to update the trusted certificates
|
||||
|
||||
fetchcerts: .PHONY
|
||||
fetch --no-sslv3 --no-tlsv1 -o certdata.txt 'https://hg.mozilla.org/projects/nss/raw-file/tip/lib/ckfw/builtins/certdata.txt'
|
||||
|
||||
cleancerts: .PHONY
|
||||
@${MAKE} -C ${.CURDIR}/trusted ${.TARGET}
|
||||
|
||||
updatecerts: .PHONY cleancerts fetchcerts
|
||||
perl ${.CURDIR}/MAca-bundle.pl -i certdata.txt -o ${.CURDIR}/trusted
|
34
secure/caroot/README
Normal file
34
secure/caroot/README
Normal file
@ -0,0 +1,34 @@
|
||||
# $FreeBSD$
|
||||
|
||||
This directory contains the scripts to update the TLS CA Root Certificates
|
||||
that comprise the 'root trust store'.
|
||||
|
||||
The 'updatecerts' make target should be run periodically by secteam@
|
||||
specifically when there is an important change to the list of trusted root
|
||||
certificates included by Mozilla.
|
||||
|
||||
It will:
|
||||
1) Remove the old trusted certificates (cleancerts)
|
||||
2) Download the latest certdata.txt from Mozilla (fetchcerts)
|
||||
3) Split certdata.txt into the individual .pem files (updatecerts)
|
||||
|
||||
Then the results should manually be inspected (svn status)
|
||||
1) Any no-longer-trusted certificates should be moved to the
|
||||
blacklisted directory (svn mv)
|
||||
2) any newly added certificates will need to be added (svn add)
|
||||
|
||||
|
||||
The following make targets exist:
|
||||
|
||||
cleancerts:
|
||||
Delete the old certificates, run as a dependency of updatecerts.
|
||||
|
||||
fetchcerts:
|
||||
Download the latest certdata.txt from the Mozilla NSS hg repo
|
||||
See the changelog here:
|
||||
https://hg.mozilla.org/projects/nss/log/tip/lib/ckfw/builtins/certdata.txt
|
||||
|
||||
updatecerts:
|
||||
Runs a perl script (MAca-bundle.pl) on the downloaded certdata.txt
|
||||
to generate the individual certificate files (.pem) and store them
|
||||
in the trusted/ directory.
|
7
secure/caroot/blacklisted/Makefile
Normal file
7
secure/caroot/blacklisted/Makefile
Normal file
@ -0,0 +1,7 @@
|
||||
# $FreeBSD$
|
||||
|
||||
BINDIR= /usr/share/certs/blacklisted
|
||||
|
||||
FILES=
|
||||
|
||||
.include <bsd.prog.mk>
|
12
secure/caroot/trusted/Makefile
Normal file
12
secure/caroot/trusted/Makefile
Normal file
@ -0,0 +1,12 @@
|
||||
# $FreeBSD$
|
||||
|
||||
BINDIR= /usr/share/certs/trusted
|
||||
|
||||
TRUSTED_CERTS!= ls ${.CURDIR}/*.pem 2> /dev/null || true
|
||||
|
||||
FILES+= ${TRUSTED_CERTS}
|
||||
|
||||
cleancerts:
|
||||
@[ -z "${TRUSTED_CERTS}" ] || rm ${TRUSTED_CERTS}
|
||||
|
||||
.include <bsd.prog.mk>
|
@ -77,6 +77,7 @@ __DEFAULT_YES_OPTIONS = \
|
||||
BZIP2 \
|
||||
CALENDAR \
|
||||
CAPSICUM \
|
||||
CAROOT \
|
||||
CASPER \
|
||||
CCD \
|
||||
CDDL \
|
||||
|
Loading…
Reference in New Issue
Block a user