svn commit: r352948 - in head: etc/mtree secure secure/caroot secure/caroot/blacklisted secure/caroot/trusted share/mk

Kyle Evans kevans at FreeBSD.org
Wed Oct 2 01:05:31 UTC 2019


Author: kevans
Date: Wed Oct  2 01:05:29 2019
New Revision: 352948
URL: https://svnweb.freebsd.org/changeset/base/352948

Log:
  [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

Added:
  head/secure/caroot/
  head/secure/caroot/MAca-bundle.pl   (contents, props changed)
  head/secure/caroot/Makefile   (contents, props changed)
  head/secure/caroot/README   (contents, props changed)
  head/secure/caroot/blacklisted/
  head/secure/caroot/blacklisted/Makefile   (contents, props changed)
  head/secure/caroot/trusted/
  head/secure/caroot/trusted/Makefile   (contents, props changed)
Modified:
  head/etc/mtree/BSD.usr.dist
  head/secure/Makefile
  head/share/mk/src.opts.mk

Modified: head/etc/mtree/BSD.usr.dist
==============================================================================
--- head/etc/mtree/BSD.usr.dist	Tue Oct  1 23:28:22 2019	(r352947)
+++ head/etc/mtree/BSD.usr.dist	Wed Oct  2 01:05:29 2019	(r352948)
@@ -194,6 +194,12 @@
             uk_UA.KOI8-U
             ..
         ..
+        certs
+            blacklisted
+            ..
+            trusted
+            ..
+        ..
         dict
         ..
         doc

Modified: head/secure/Makefile
==============================================================================
--- head/secure/Makefile	Tue Oct  1 23:28:22 2019	(r352947)
+++ head/secure/Makefile	Wed Oct  2 01:05:29 2019	(r352948)
@@ -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	\

Added: head/secure/caroot/MAca-bundle.pl
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/secure/caroot/MAca-bundle.pl	Wed Oct  2 01:05:29 2019	(r352948)
@@ -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 at FreeBSD.org>
+##  All rights reserved.
+##  Copyright (c) 2018, Allan Jude <allanjude at 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, at 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";

Added: head/secure/caroot/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/secure/caroot/Makefile	Wed Oct  2 01:05:29 2019	(r352948)
@@ -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

Added: head/secure/caroot/README
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/secure/caroot/README	Wed Oct  2 01:05:29 2019	(r352948)
@@ -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.

Added: head/secure/caroot/blacklisted/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/secure/caroot/blacklisted/Makefile	Wed Oct  2 01:05:29 2019	(r352948)
@@ -0,0 +1,7 @@
+# $FreeBSD$
+
+BINDIR=		/usr/share/certs/blacklisted
+
+FILES=
+
+.include <bsd.prog.mk>

Added: head/secure/caroot/trusted/Makefile
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/secure/caroot/trusted/Makefile	Wed Oct  2 01:05:29 2019	(r352948)
@@ -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>

Modified: head/share/mk/src.opts.mk
==============================================================================
--- head/share/mk/src.opts.mk	Tue Oct  1 23:28:22 2019	(r352947)
+++ head/share/mk/src.opts.mk	Wed Oct  2 01:05:29 2019	(r352948)
@@ -77,6 +77,7 @@ __DEFAULT_YES_OPTIONS = \
     BZIP2 \
     CALENDAR \
     CAPSICUM \
+    CAROOT \
     CASPER \
     CCD \
     CDDL \


More information about the svn-src-all mailing list