diff --git a/etc/mtree/BSD.usr.dist b/etc/mtree/BSD.usr.dist index 32d2c197716c..5ae56e1de121 100644 --- a/etc/mtree/BSD.usr.dist +++ b/etc/mtree/BSD.usr.dist @@ -194,6 +194,12 @@ uk_UA.KOI8-U .. .. + certs + blacklisted + .. + trusted + .. + .. dict .. doc diff --git a/secure/Makefile b/secure/Makefile index c8d12502740d..198df326fe32 100644 --- a/secure/Makefile +++ b/secure/Makefile @@ -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 \ diff --git a/secure/caroot/MAca-bundle.pl b/secure/caroot/MAca-bundle.pl new file mode 100755 index 000000000000..c84e83a3c730 --- /dev/null +++ b/secure/caroot/MAca-bundle.pl @@ -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 +## All rights reserved. +## Copyright (c) 2018, Allan Jude +## +## 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 <) { + 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"; diff --git a/secure/caroot/Makefile b/secure/caroot/Makefile new file mode 100644 index 000000000000..c9d39d71bad9 --- /dev/null +++ b/secure/caroot/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +PACKAGE= caroot + +CLEANFILES+= certdata.txt + +SUBDIR+= trusted +SUBDIR+= blacklisted + +.include + +# 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 diff --git a/secure/caroot/README b/secure/caroot/README new file mode 100644 index 000000000000..9a4fc0320e2a --- /dev/null +++ b/secure/caroot/README @@ -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. diff --git a/secure/caroot/blacklisted/Makefile b/secure/caroot/blacklisted/Makefile new file mode 100644 index 000000000000..7691993416b3 --- /dev/null +++ b/secure/caroot/blacklisted/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +BINDIR= /usr/share/certs/blacklisted + +FILES= + +.include diff --git a/secure/caroot/trusted/Makefile b/secure/caroot/trusted/Makefile new file mode 100644 index 000000000000..f159edddff05 --- /dev/null +++ b/secure/caroot/trusted/Makefile @@ -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 diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk index fcba0a0f184a..dc928e2e19b3 100644 --- a/share/mk/src.opts.mk +++ b/share/mk/src.opts.mk @@ -77,6 +77,7 @@ __DEFAULT_YES_OPTIONS = \ BZIP2 \ CALENDAR \ CAPSICUM \ + CAROOT \ CASPER \ CCD \ CDDL \