diff --git a/.github/workflows/update-certs.yml b/.github/workflows/update-certs.yml index 1060b19e70..af770fb800 100644 --- a/.github/workflows/update-certs.yml +++ b/.github/workflows/update-certs.yml @@ -23,8 +23,16 @@ jobs: - name: Checkout code uses: actions/checkout@2541b1294d2704b0964813337f33b291d3f8596b # v.24.0 + # We trust own version of mk-ca-bundle.pl and its output, but as an extra check + # we compare with the cacert.pem served by https://curl.se/ca/cacert.pem (this + # allows detecting updates in the mk-ca-bundle.pl or any issues with the output + # of mk-ca-bundle.pl). - name: Update certs - run: cd orbit/pkg/packaging && ./mk-ca-bundle.pl -u certs.pem + run: | + cd orbit/pkg/packaging && ./mk-ca-bundle.pl -u certs.pem + curl https://curl.se/ca/cacert.pem --output cacert.pem + diff --ignore-matching-lines "Certificate data from Mozilla as of*" certs.pem cacert.pem + rm cacert.pem - name: PR changes uses: peter-evans/create-pull-request@f22a7da129c901513876a2380e2dae9f8e145330 # v3.12.1 diff --git a/orbit/pkg/packaging/certs.pem b/orbit/pkg/packaging/certs.pem index 11a51cfa85..fd4f6d189d 100644 --- a/orbit/pkg/packaging/certs.pem +++ b/orbit/pkg/packaging/certs.pem @@ -1,7 +1,7 @@ ## ## Bundle of CA Root Certificates ## -## Certificate data from Mozilla as of: Tue Jul 19 06:03:20 2022 GMT +## Certificate data from Mozilla as of: Wed Oct 12 12:35:08 2022 GMT ## ## This is a bundle of X.509 certificates of public Certificate Authorities ## (CA). These were automatically extracted from Mozilla's root certificates @@ -13,8 +13,8 @@ ## an Apache+mod_ssl webserver for SSL client authentication. ## Just configure this file as the SSLCACertificateFile. ## -## Conversion done with mk-ca-bundle.pl version 1.28. -## SHA256: 9bf3799611fb58197f61d45e71ce3dc19f30e7dd73731915872ce5108a7bb066 +## Conversion done with mk-ca-bundle.pl version 1.29. +## SHA256: 3ff8bd209b5f2e739b9f2b96eacb694a774114685b02978257824f37ff528f71 ## @@ -993,36 +993,6 @@ tnRGEmyR7jTV7JqR50S+kDFy1UkC9gLl9B/rfNmWVan/7Ir5mUf/NVoCqgTLiluHcSmRvaS0eg29 mvVXIwAHIRc/SjnRBUkLp7Y3gaVdjKozXoEofKd9J+sAro03 -----END CERTIFICATE----- -EC-ACC -====== ------BEGIN CERTIFICATE----- -MIIFVjCCBD6gAwIBAgIQ7is969Qh3hSoYqwE893EATANBgkqhkiG9w0BAQUFADCB8zELMAkGA1UE -BhMCRVMxOzA5BgNVBAoTMkFnZW5jaWEgQ2F0YWxhbmEgZGUgQ2VydGlmaWNhY2lvIChOSUYgUS0w -ODAxMTc2LUkpMSgwJgYDVQQLEx9TZXJ2ZWlzIFB1YmxpY3MgZGUgQ2VydGlmaWNhY2lvMTUwMwYD -VQQLEyxWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5ldC92ZXJhcnJlbCAoYykwMzE1MDMGA1UE -CxMsSmVyYXJxdWlhIEVudGl0YXRzIGRlIENlcnRpZmljYWNpbyBDYXRhbGFuZXMxDzANBgNVBAMT -BkVDLUFDQzAeFw0wMzAxMDcyMzAwMDBaFw0zMTAxMDcyMjU5NTlaMIHzMQswCQYDVQQGEwJFUzE7 -MDkGA1UEChMyQWdlbmNpYSBDYXRhbGFuYSBkZSBDZXJ0aWZpY2FjaW8gKE5JRiBRLTA4MDExNzYt -SSkxKDAmBgNVBAsTH1NlcnZlaXMgUHVibGljcyBkZSBDZXJ0aWZpY2FjaW8xNTAzBgNVBAsTLFZl -Z2V1IGh0dHBzOi8vd3d3LmNhdGNlcnQubmV0L3ZlcmFycmVsIChjKTAzMTUwMwYDVQQLEyxKZXJh -cnF1aWEgRW50aXRhdHMgZGUgQ2VydGlmaWNhY2lvIENhdGFsYW5lczEPMA0GA1UEAxMGRUMtQUND -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsyLHT+KXQpWIR4NA9h0X84NzJB5R85iK -w5K4/0CQBXCHYMkAqbWUZRkiFRfCQ2xmRJoNBD45b6VLeqpjt4pEndljkYRm4CgPukLjbo73FCeT -ae6RDqNfDrHrZqJyTxIThmV6PttPB/SnCWDaOkKZx7J/sxaVHMf5NLWUhdWZXqBIoH7nF2W4onW4 -HvPlQn2v7fOKSGRdghST2MDk/7NQcvJ29rNdQlB50JQ+awwAvthrDk4q7D7SzIKiGGUzE3eeml0a -E9jD2z3Il3rucO2n5nzbcc8tlGLfbdb1OL4/pYUKGbio2Al1QnDE6u/LDsg0qBIimAy4E5S2S+zw -0JDnJwIDAQABo4HjMIHgMB0GA1UdEQQWMBSBEmVjX2FjY0BjYXRjZXJ0Lm5ldDAPBgNVHRMBAf8E -BTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUoMOLRKo3pUW/l4Ba0fF4opvpXY0wfwYD -VR0gBHgwdjB0BgsrBgEEAfV4AQMBCjBlMCwGCCsGAQUFBwIBFiBodHRwczovL3d3dy5jYXRjZXJ0 -Lm5ldC92ZXJhcnJlbDA1BggrBgEFBQcCAjApGidWZWdldSBodHRwczovL3d3dy5jYXRjZXJ0Lm5l -dC92ZXJhcnJlbCAwDQYJKoZIhvcNAQEFBQADggEBAKBIW4IB9k1IuDlVNZyAelOZ1Vr/sXE7zDkJ -lF7W2u++AVtd0x7Y/X1PzaBB4DSTv8vihpw3kpBWHNzrKQXlxJ7HNd+KDM3FIUPpqojlNcAZQmNa -Al6kSBg6hW/cnbw/nZzBh7h6YQjpdwt/cKt63dmXLGQehb+8dJahw3oS7AwaboMMPOhyRp/7SNVe -l+axofjk70YllJyJ22k4vuxcDlbHZVHlUIiIv0LVKz3l+bqeLrPK9HOSAgu+TGbrIP65y7WZf+a2 -E/rKS03Z7lNGBjvGTq2TWoF+bCpLagVFjPIhpDGQh2xlnJ2lYJU6Un/10asIbvPuW/mIPX64b24D -5EI= ------END CERTIFICATE----- - Actalis Authentication Root CA ============================== -----BEGIN CERTIFICATE----- @@ -3488,3 +3458,49 @@ zPUwHQYDVR0OBBYEFP+CMXI++cRmbK04ntGwUYilkMz1MA4GA1UdDwEB/wQEAwIBBjAKBggqhkjO PQQDAwNpADBmAjEA5gVYaWHlLcoNy/EZCL3W/VGSGn5jVASQkZo1kTmZ+gepZpO6yGjUij/67W4W Aie3AjEA3VoXK3YdZUKWpqxdinlW2Iob35reX8dQj7FbcQwm32pAAOwzkSFxvmjkI6TZraE3 -----END CERTIFICATE----- + +Security Communication RootCA3 +============================== +-----BEGIN CERTIFICATE----- +MIIFfzCCA2egAwIBAgIJAOF8N0D9G/5nMA0GCSqGSIb3DQEBDAUAMF0xCzAJBgNVBAYTAkpQMSUw +IwYDVQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMScwJQYDVQQDEx5TZWN1cml0eSBD +b21tdW5pY2F0aW9uIFJvb3RDQTMwHhcNMTYwNjE2MDYxNzE2WhcNMzgwMTE4MDYxNzE2WjBdMQsw +CQYDVQQGEwJKUDElMCMGA1UEChMcU0VDT00gVHJ1c3QgU3lzdGVtcyBDTy4sTFRELjEnMCUGA1UE +AxMeU2VjdXJpdHkgQ29tbXVuaWNhdGlvbiBSb290Q0EzMIICIjANBgkqhkiG9w0BAQEFAAOCAg8A +MIICCgKCAgEA48lySfcw3gl8qUCBWNO0Ot26YQ+TUG5pPDXC7ltzkBtnTCHsXzW7OT4rCmDvu20r +hvtxosis5FaU+cmvsXLUIKx00rgVrVH+hXShuRD+BYD5UpOzQD11EKzAlrenfna84xtSGc4RHwsE +NPXY9Wk8d/Nk9A2qhd7gCVAEF5aEt8iKvE1y/By7z/MGTfmfZPd+pmaGNXHIEYBMwXFAWB6+oHP2 +/D5Q4eAvJj1+XCO1eXDe+uDRpdYMQXF79+qMHIjH7Iv10S9VlkZ8WjtYO/u62C21Jdp6Ts9EriGm +npjKIG58u4iFW/vAEGK78vknR+/RiTlDxN/e4UG/VHMgly1s2vPUB6PmudhvrvyMGS7TZ2crldtY +XLVqAvO4g160a75BflcJdURQVc1aEWEhCmHCqYj9E7wtiS/NYeCVvsq1e+F7NGcLH7YMx3weGVPK +p7FKFSBWFHA9K4IsD50VHUeAR/94mQ4xr28+j+2GaR57GIgUssL8gjMunEst+3A7caoreyYn8xrC +3PsXuKHqy6C0rtOUfnrQq8PsOC0RLoi/1D+tEjtCrI8Cbn3M0V9hvqG8OmpI6iZVIhZdXw3/JzOf +GAN0iltSIEdrRU0id4xVJ/CvHozJgyJUt5rQT9nO/NkuHJYosQLTA70lUhw0Zk8jq/R3gpYd0Vcw +CBEF/VfR2ccCAwEAAaNCMEAwHQYDVR0OBBYEFGQUfPxYchamCik0FW8qy7z8r6irMA4GA1UdDwEB +/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBDAUAA4ICAQDcAiMI4u8hOscNtybS +YpOnpSNyByCCYN8Y11StaSWSntkUz5m5UoHPrmyKO1o5yGwBQ8IibQLwYs1OY0PAFNr0Y/Dq9HHu +Tofjcan0yVflLl8cebsjqodEV+m9NU1Bu0soo5iyG9kLFwfl9+qd9XbXv8S2gVj/yP9kaWJ5rW4O +H3/uHWnlt3Jxs/6lATWUVCvAUm2PVcTJ0rjLyjQIUYWg9by0F1jqClx6vWPGOi//lkkZhOpn2ASx +YfQAW0q3nHE3GYV5v4GwxxMOdnE+OoAGrgYWp421wsTL/0ClXI2lyTrtcoHKXJg80jQDdwj98ClZ +XSEIx2C/pHF7uNkegr4Jr2VvKKu/S7XuPghHJ6APbw+LP6yVGPO5DtxnVW5inkYO0QR4ynKudtml ++LLfiAlhi+8kTtFZP1rUPcmTPCtk9YENFpb3ksP+MW/oKjJ0DvRMmEoYDjBU1cXrvMUVnuiZIesn +KwkK2/HmcBhWuwzkvvnoEKQTkrgc4NtnHVMDpCKn3F2SEDzq//wbEBrD2NCcnWXL0CsnMQMeNuE9 +dnUM/0Umud1RvCPHX9jYhxBAEg09ODfnRDwYwFMJZI//1ZqmfHAuc1Uh6N//g7kdPjIe1qZ9LPFm +6Vwdp6POXiUyK+OVrCoHzrQoeIY8LaadTdJ0MN1kURXbg4NR16/9M51NZg== +-----END CERTIFICATE----- + +Security Communication ECC RootCA1 +================================== +-----BEGIN CERTIFICATE----- +MIICODCCAb6gAwIBAgIJANZdm7N4gS7rMAoGCCqGSM49BAMDMGExCzAJBgNVBAYTAkpQMSUwIwYD +VQQKExxTRUNPTSBUcnVzdCBTeXN0ZW1zIENPLixMVEQuMSswKQYDVQQDEyJTZWN1cml0eSBDb21t +dW5pY2F0aW9uIEVDQyBSb290Q0ExMB4XDTE2MDYxNjA1MTUyOFoXDTM4MDExODA1MTUyOFowYTEL +MAkGA1UEBhMCSlAxJTAjBgNVBAoTHFNFQ09NIFRydXN0IFN5c3RlbXMgQ08uLExURC4xKzApBgNV +BAMTIlNlY3VyaXR5IENvbW11bmljYXRpb24gRUNDIFJvb3RDQTEwdjAQBgcqhkjOPQIBBgUrgQQA +IgNiAASkpW9gAwPDvTH00xecK4R1rOX9PVdu12O/5gSJko6BnOPpR27KkBLIE+CnnfdldB9sELLo +5OnvbYUymUSxXv3MdhDYW72ixvnWQuRXdtyQwjWpS4g8EkdtXP9JTxpKULGjQjBAMB0GA1UdDgQW +BBSGHOf+LaVKiwj+KBH6vqNm+GBZLzAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAK +BggqhkjOPQQDAwNoADBlAjAVXUI9/Lbu9zuxNuie9sRGKEkz0FhDKmMpzE2xtHqiuQ04pV1IKv3L +snNdo4gIxwwCMQDAqy0Obe0YottT6SXbVQjgUMzfRGEWgqtJsLKB7HOHeLRMsmIbEvoWTSVLY70e +N9k= +-----END CERTIFICATE----- diff --git a/orbit/pkg/packaging/mk-ca-bundle.pl b/orbit/pkg/packaging/mk-ca-bundle.pl index e5a7420c0e..926478f126 100755 --- a/orbit/pkg/packaging/mk-ca-bundle.pl +++ b/orbit/pkg/packaging/mk-ca-bundle.pl @@ -6,7 +6,7 @@ # * | (__| |_| | _ <| |___ # * \___|\___/|_| \_\_____| # * -# * Copyright (C) 1998 - 2021, Daniel Stenberg, , et al. +# * Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. # * # * This software is licensed as described in the file COPYING, which # * you should have received as part of this distribution. The terms @@ -19,6 +19,8 @@ # * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY # * KIND, either express or implied. # * +# * SPDX-License-Identifier: curl +# * # *************************************************************************** # 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), @@ -63,11 +65,12 @@ $opt_d = 'release'; # If the OpenSSL commandline is not in search path you can configure it here! my $openssl = 'openssl'; -my $version = '1.28'; +my $version = '1.29'; $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) +# 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; @@ -94,8 +97,12 @@ my @valid_mozilla_trust_purposes = ( 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). + "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"; @@ -210,8 +217,8 @@ sub is_in_list($@) { 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 +# 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; @@ -227,7 +234,8 @@ sub parse_csv_param($$@) { 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 + # 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(); } @@ -282,7 +290,8 @@ 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 + # 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}} ) ); } @@ -421,9 +430,13 @@ my $caname; my $certnum = 0; my $skipnum = 0; my $start_of_cert = 0; +my $main_block = 0; +my $main_block_name; +my $trust_block = 0; +my $trust_block_name; my @precert; my $cka_value; -my $valid = 1; +my $valid = 0; open(TXT,"$txt") or die "Couldn't open $txt: $!\n"; while () { @@ -435,101 +448,170 @@ while () { print if ($opt_l); last if (/\*\*\*\*\* END LICENSE BLOCK \*\*\*\*\*/); } + next; } -# Not Valid After : Thu Sep 30 14:01:15 2021 - elsif(/^# Not Valid After : (.*)/) { - my $stamp = $1; - use Time::Piece; - my $t = Time::Piece->strptime - ($stamp, "%a %b %d %H:%M:%S %Y"); - my $delta = ($t->epoch - time()); # negative means no longer valid - if($delta < 0) { + # The input file format consists of blocks of Mozilla objects. + # The blocks are separated by blank lines but may be related. + elsif(/^\s*$/) { + $main_block = 0; + $trust_block = 0; + next; + } + # Each certificate has a main block. + elsif(/^# Certificate "(.*)"/) { + (!$main_block && !$trust_block) or die "Unexpected certificate block"; + $main_block = 1; + $main_block_name = $1; + # Reset all other certificate variables. + $trust_block = 0; + $trust_block_name = ""; + $valid = 0; + $start_of_cert = 0; + $caname = ""; + $cka_value = ""; + undef @precert; + next; + } + # Each certificate's main block is followed by a trust block. + elsif(/^# Trust for (?:Certificate )?"(.*)"/) { + (!$main_block && !$trust_block) or die "Unexpected trust block"; + $trust_block = 1; + $trust_block_name = $1; + if($main_block_name ne $trust_block_name) { + die "cert name \"$main_block_name\" != trust name \"$trust_block_name\""; + } + next; + } + # Ignore other blocks. + # + # There is a documentation comment block, a BEGINDATA block, and a bunch of + # blocks starting with "# Explicitly Distrust ". + # + # The latter is for certificates that have already been removed and are not + # included. Not all explicitly distrusted certificates are ignored at this + # point, just those without an actual certificate. + elsif(!$main_block && !$trust_block) { + next; + } + elsif(/^#/) { + # The commented lines in a main block are plaintext metadata that describes + # the certificate. Issuer, Subject, Fingerprint, etc. + if($main_block) { + push @precert, $_ if not /^#$/; + if(/^# Not Valid After : (.*)/) { + my $stamp = $1; + use Time::Piece; + # Not Valid After : Thu Sep 30 14:01:15 2021 + my $t = Time::Piece->strptime($stamp, "%a %b %d %H:%M:%S %Y"); + my $delta = ($t->epoch - time()); # negative means no longer valid + if($delta < 0) { $skipnum++; - report "Skipping: $caname is not valid anymore" if ($opt_v); + report "Skipping: $main_block_name is not valid anymore" if ($opt_v); $valid = 0; - } - else { + } + else { $valid = 1; - } - next; - } - elsif(/^# (Issuer|Serial Number|Subject|Not Valid Before|Fingerprint \(MD5\)|Fingerprint \(SHA1\)):/) { - push @precert, $_; - 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 () { - 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 () { - last if (/^END/); - chomp; - my @octets = split(/\\/); - shift @octets; - for (@octets) { - $cka_value .= chr(oct); + } } } + next; } - if(/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/ && $valid) { + elsif(!$valid) { + next; + } + + chomp; + + if($main_block) { + if(/^CKA_CLASS CK_OBJECT_CLASS CKO_CERTIFICATE/) { + !$start_of_cert or die "Duplicate CKO_CERTIFICATE object"; + $start_of_cert = 1; + next; + } + elsif(!$start_of_cert) { + next; + } + elsif(/^CKA_LABEL UTF8 \"(.*)\"/) { + ($caname eq "") or die "Duplicate CKA_LABEL attribute"; + $caname = $1; + if($caname ne $main_block_name) { + die "caname \"$caname\" != cert name \"$main_block_name\""; + } + next; + } + elsif(/^CKA_VALUE MULTILINE_OCTAL/) { + ($cka_value eq "") or die "Duplicate CKA_VALUE attribute"; + while () { + last if (/^END/); + chomp; + my @octets = split(/\\/); + shift @octets; + for (@octets) { + $cka_value .= chr(oct); + } + } + next; + } + elsif (/^CKA_NSS_SERVER_DISTRUST_AFTER (CK_BBOOL CK_FALSE|MULTILINE_OCTAL)/) { + # Example: + # CKA_NSS_SERVER_DISTRUST_AFTER MULTILINE_OCTAL + # \062\060\060\066\061\067\060\060\060\060\060\060\132 + # END + if($1 eq "MULTILINE_OCTAL") { + my @timestamp; + while () { + last if (/^END/); + chomp; + my @octets = split(/\\/); + shift @octets; + for (@octets) { + push @timestamp, chr(oct); + } + } + scalar(@timestamp) == 13 or die "Failed parsing timestamp"; + # 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: $main_block_name is not trusted anymore" if ($opt_v); + $valid = 0; + } + else { + # still trusted + } + } + next; + } + else { + next; + } + } + + if(!$trust_block || !$start_of_cert || $caname eq "" || $cka_value eq "") { + die "Certificate extraction failed"; + } + + my %trust_purposes_by_level; + + if(/^CKA_CLASS CK_OBJECT_CLASS CKO_NSS_TRUST/) { # now scan the trust part to determine how we should trust this cert while () { - last if (/^#/); + if(/^\s*$/) { + $trust_block = 0; + last; + } 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"; @@ -541,33 +623,42 @@ while () { } } + # Sanity check that an explicitly distrusted certificate only has trust + # purposes with a trust level of NOT_TRUSTED. + # + # Certificate objects that are explicitly distrusted are in a certificate + # block that starts # Certificate "Explicitly Distrust(ed) ", + # where "Explicitly Distrust(ed) " was prepended to the original cert name. + if($caname =~ /distrust/i || + $main_block_name =~ /distrust/i || + $trust_block_name =~ /distrust/i) { + my @levels = keys %trust_purposes_by_level; + if(scalar(@levels) != 1 || $levels[0] ne "NOT_TRUSTED") { + die "\"$caname\" must have all trust purposes at level NOT_TRUSTED."; + } + } + if ( !should_output_cert(%trust_purposes_by_level) ) { $skipnum ++; - report "Skipping: $caname" if ($opt_v); + report "Skipping: $caname lacks acceptable trust level" if ($opt_v); } else { - my $data = $cka_value; - $cka_value = ""; - - if(!length($data)) { - # if empty, skip - next; - } - my $encoded = MIME::Base64::encode_base64($data, ''); + my $encoded = MIME::Base64::encode_base64($cka_value, ''); $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)); + print CRT ("=" x $maxStringLength . "\n"); if ($opt_t) { foreach my $key (sort 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_m) { + print CRT for @precert; + } if (!$opt_t) { print CRT $pem; } else { @@ -597,13 +688,10 @@ while () { open(CRT, ">>$crt.~") or die "Couldn't open $crt.~: $!"; } } - report "Parsing: $caname" if ($opt_v); + report "Processed: $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";