blob: 2320b4c53cd5960f49031ea33105f937c06bba1d [file] [log] [blame]
From ef26f0407eed02a3b48775689c9ffe6c0aef5f10 Mon Sep 17 00:00:00 2001
From: Mattias Nissler <mnissler@chromium.org>
Date: Mon, 4 Nov 2019 20:47:18 +0100
Subject: [PATCH] Support for blocklisting certificates for verification
We can add certs to the blocklist by serial (with 'serial <n>') or by
sha256sum with ('sha256 <n>').
This is a forward-port of the original blocklisting patch for 1.0.2 by
ellyjones@chromium.org. The code is somewhat problematic (for example
it reads the blocklist file for each certificate it checks, only works
for the internal verification code path we happen to use, doesn't have
proper error handling), so ideally we'd find a better solution to do
blocklisting moving forward. Alas, this is what we have right now.
BUG=chromium:203154
TEST=unit test,security_OpenSSLBlocklist
TEST=FEATURES=test emerge openssl
TEST=tast run <target> security.OpenSSLBlocklist
---
crypto/x509/x509_vfy.c | 125 ++++++++++++++++++
test/recipes/90-test_blocklist.t | 71 ++++++++++
.../90-test_blocklist_data/globalsign.pem | 22 +++
.../recipes/90-test_blocklist_data/google.pem | 53 ++++++++
test/recipes/90-test_blocklist_data/gts.pem | 25 ++++
5 files changed, 296 insertions(+)
create mode 100644 test/recipes/90-test_blocklist.t
create mode 100644 test/recipes/90-test_blocklist_data/globalsign.pem
create mode 100644 test/recipes/90-test_blocklist_data/google.pem
create mode 100644 test/recipes/90-test_blocklist_data/gts.pem
--- a/crypto/x509/x509_vfy.c
+++ b/crypto/x509/x509_vfy.c
@@ -250,6 +250,127 @@ static int verify_chain(X509_STORE_CTX *ctx)
return ok;
}
+/* A version of fgets() that returns the first sz - 1 characters of the next
+ * line from 'in'. The rest of the line is discarded. */
+static int gets_trunc(BIO *in, char *buf, int sz)
+{
+ char b;
+ int i = 0;
+
+ if (sz < 1)
+ return i;
+
+ while (BIO_read(in, &b, 1)) {
+ if (i < sz - 1)
+ buf[i++] = b;
+ if (!b || b == '\n')
+ break;
+ }
+
+ buf[i] = '\0';
+
+ return i;
+}
+
+/* Converts a byte string into a hex string, null-terminated. The 'out' buffer
+ * must be at least 2 * insize + 1 bytes long. */
+static void hexify(unsigned char *in, char *out, int insize)
+{
+ int i;
+ static const char hex[] = "0123456789abcdef";
+ for (i = 0; i < insize; i++) {
+ out[i * 2] = hex[in[i] >> 4];
+ out[i * 2 + 1] = hex[in[i] & 0xf];
+ }
+ out[i * 2] = '\0';
+}
+
+static int is_blocklisted(X509 *x)
+{
+ /* See http://tools.ietf.org/html/rfc5280#section-4.1.2.2:
+ * "Certificate users MUST be able to handle serialNumber values up to
+ * 20 octets. Conforming CAs MUST NOT use serialNumber values longer
+ * than 20 octets."
+ */
+ static const int MAX_SERIAL = 20;
+ static const int MAX_BLOCKLIST_LINE = 1024;
+
+ unsigned char md[EVP_MAX_MD_SIZE];
+ char hexsha256[EVP_MAX_MD_SIZE * 2 + 1];
+ char hexsha1[EVP_MAX_MD_SIZE * 2 + 1];
+ char hexserial[MAX_SERIAL * 2 + 1];
+ const EVP_MD *sha256 = EVP_sha256();
+ const EVP_MD *sha1 = EVP_sha1();
+ unsigned int n;
+ char line[MAX_BLOCKLIST_LINE];
+ BIO *file;
+ int ret = 0;
+ ASN1_INTEGER *serial = NULL;
+ unsigned int serial_len;
+ const char *path = getenv("OPENSSL_BLOCKLIST_PATH");
+
+ if (!path)
+ path = OPENSSLDIR "/blocklist";
+
+ file = BIO_new_file(path, "r");
+ if (!file) {
+ ERR_clear_error();
+ return 0;
+ }
+
+ if (!X509_digest(x, sha256, md, &n))
+ goto out;
+ hexify(md, hexsha256, n);
+ if (!X509_digest(x, sha1, md, &n))
+ goto out;
+ hexify(md, hexsha1, n);
+ serial = X509_get_serialNumber(x);
+ serial_len = serial->length;
+ if (serial_len > sizeof(hexserial) / 2)
+ /* We only match the first MAX_SERIAL bytes of the serial. */
+ serial_len = sizeof(hexserial) / 2;
+ hexify(serial->data, hexserial, serial_len);
+
+ while (gets_trunc(file, line, sizeof(line))) {
+ char *p, *str = line;
+ char *cmd = strsep(&str, " ");
+ char *arg = strsep(&str, " ");
+ if (!cmd || !arg || cmd[0] == '#')
+ continue;
+ if ((p = strchr(arg, '\n')))
+ *p = '\0';
+ if (!strcmp(cmd, "sha256") && !strcmp(arg, hexsha256)) {
+ ret = 1;
+ goto out;
+ }
+ if (!strcmp(cmd, "sha1") && !strcmp(arg, hexsha1)) {
+ ret = 1;
+ goto out;
+ }
+ if (!strcmp(cmd, "serial") && !strcmp(arg, hexserial)) {
+ ret = 1;
+ goto out;
+ }
+ }
+
+out:
+ BIO_free(file);
+ return ret;
+}
+
+static int check_blocklist(X509_STORE_CTX *ctx)
+{
+ int i;
+ X509 *x;
+
+ for (i = 0; i < ctx->num_untrusted; i++) {
+ x = sk_X509_value(ctx->chain, i);
+ if (is_blocklisted(x))
+ return 0;
+ }
+ return 1;
+}
+
int X509_verify_cert(X509_STORE_CTX *ctx)
{
SSL_DANE *dane = ctx->dane;
@@ -3193,6 +3314,10 @@ static int build_chain(X509_STORE_CTX *ctx)
trust = check_trust(ctx, num);
}
+ if (!check_blocklist(ctx)) {
+ trust = X509_TRUST_REJECTED;
+ }
+
switch (trust) {
case X509_TRUST_TRUSTED:
return 1;
--- /dev/null
+++ b/test/recipes/90-test_blocklist.t
@@ -0,0 +1,71 @@
+#! /usr/bin/env perl
+# Copyright 2019 The OpenSSL Project Authors. All Rights Reserved.
+#
+# Licensed under the OpenSSL license (the "License"). You may not use
+# this file except in compliance with the License. You can obtain a copy
+# in the file LICENSE in the source distribution or at
+# https://www.openssl.org/source/license.html
+
+use strict;
+use warnings;
+
+use File::Spec::Functions qw/catfile curdir/;
+use OpenSSL::Test;
+use OpenSSL::Test::Utils;
+use OpenSSL::Test qw/:DEFAULT with data_file/;
+
+setup("test_blocklist");
+
+# Fix time to 2019-11-01 for predictable validity.
+my @check_time=("-attime", "1572562800");
+
+sub test_blocklist {
+ my $desc = shift;
+ my $blocklist = shift;
+ my $expected_exit = shift;
+
+ # Use a blocklist file in the test-runs directory.
+ my $blocklist_file = catfile(curdir(), "blocklist");
+ $ENV{OPENSSL_BLOCKLIST_PATH} = $blocklist_file;
+
+ open(my $blocklist_fh, ">", $blocklist_file);
+ print $blocklist_fh $blocklist;
+ close $blocklist_fh;
+
+ with({ exit_checker => sub { return shift == $expected_exit; } },
+ sub { ok(run(app(["openssl", "verify", @check_time,
+ "-CAfile", data_file("globalsign.pem"),
+ "-untrusted", data_file("gts.pem"),
+ data_file("google.pem")])),
+ $desc);
+ });
+
+ unlink $blocklist_file;
+}
+
+plan tests => 3;
+
+subtest "=== Blocklist: Successful chain verification ===" => sub {
+ plan tests => 2;
+
+ test_blocklist("no blocklist", "", 0);
+
+ my $h = "60f1da1ee6967650f8e3f0d017effd9864d439367ee6839c999e668bd2b08131";
+ test_blocklist("non-matching sha256", "sha256 " + $h, 0);
+};
+
+subtest "=== Blocklist: Intermediate ===" => sub {
+ plan tests => 3;
+
+ test_blocklist("serial", "serial 01e3b49aa18d8aa981256950b8", 2);
+ test_blocklist("sha1", "sha1 dfe2070c79e7ff36a925ffa327ffe3deecf8f9c2", 2);
+ test_blocklist("sha256", "sha256 95c074e35902a14abd9d19afb6e7f80e669ff8e2363270539d963613f04aaa21", 2);
+};
+
+subtest "=== Blocklist: Leaf ===" => sub {
+ plan tests => 3;
+
+ test_blocklist("serial", "serial eaab738ecc290675020000000047d911", 2);
+ test_blocklist("sha1", "sha1 0fd9151c4d4a317b647e87713bd7226b8b4fcbda", 2);
+ test_blocklist("sha256", "sha256 49905184c9d70d7c850709f5bfa3f7c966dbe391e8e5d8aea1c11be942dcf0bb", 2);
+};
--- /dev/null
+++ b/test/recipes/90-test_blocklist_data/globalsign.pem
@@ -0,0 +1,22 @@
+-----BEGIN CERTIFICATE-----
+MIIDujCCAqKgAwIBAgILBAAAAAABD4Ym5g0wDQYJKoZIhvcNAQEFBQAwTDEgMB4G
+A1UECxMXR2xvYmFsU2lnbiBSb290IENBIC0gUjIxEzARBgNVBAoTCkdsb2JhbFNp
+Z24xEzARBgNVBAMTCkdsb2JhbFNpZ24wHhcNMDYxMjE1MDgwMDAwWhcNMjExMjE1
+MDgwMDAwWjBMMSAwHgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEG
+A1UEChMKR2xvYmFsU2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjCCASIwDQYJKoZI
+hvcNAQEBBQADggEPADCCAQoCggEBAKbPJA6+Lm8omUVCxKs+IVSbC9N/hHD6ErPL
+v4dfxn+G07IwXNb9rfF73OX4YJYJkhD10FPe+3t+c4isUoh7SqbKSaZeqKeMWhG8
+eoLrvozps6yWJQeXSpkqBy+0Hne/ig+1AnwblrjFuTosvNYSuetZfeLQBoZfXklq
+tTleiDTsvHgMCJiEbKjNS7SgfQx5TfC4LcshytVsW33hoCmEofnTlEnLJGKRILzd
+C9XZzPnqJworc5HGnRusyMvo4KD0L5CLTfuwNhv2GXqF4G3yYROIXJ/gkwpRl4pa
+zq+r1feqCapgvdzZX99yqWATXgAByUr6P6TqBwMhAo6CygPCm48CAwEAAaOBnDCB
+mTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUm+IH
+V2ccHsBqBt5ZtJot39wZhi4wNgYDVR0fBC8wLTAroCmgJ4YlaHR0cDovL2NybC5n
+bG9iYWxzaWduLm5ldC9yb290LXIyLmNybDAfBgNVHSMEGDAWgBSb4gdXZxwewGoG
+3lm0mi3f3BmGLjANBgkqhkiG9w0BAQUFAAOCAQEAmYFThxxol4aR7OBKuEQLq4Gs
+J0/WwbgcQ3izDJr86iw8bmEbTUsp9Z8FHSbBuOmDAGJFtqkIk7mpM0sYmsL4h4hO
+291xNBrBVNpGP+DTKqttVCL1OmLNIG+6KYnX3ZHu01yiPqFbQfXf5WRDLenVOavS
+ot+3i9DAgBkcRcAtjOj4LaR0VknFBbVPFd5uRHg5h6h+u/N5GJG79G+dwfCMNYxd
+AfvDbbnvRG15RjF+Cv6pgsH/76tuIMRQyV+dTZsXjAzlAcmgQWpzU/qlULRuJQ/7
+TBj0/VLZjmmx6BEP3ojY+x1J96relc8geMJgEtslQIxq/H5COEBkEveegeGTLg==
+-----END CERTIFICATE-----
--- /dev/null
+++ b/test/recipes/90-test_blocklist_data/google.pem
@@ -0,0 +1,53 @@
+-----BEGIN CERTIFICATE-----
+MIIJRTCCCC2gAwIBAgIRAOqrc47MKQZ1AgAAAABH2REwDQYJKoZIhvcNAQELBQAw
+QjELMAkGA1UEBhMCVVMxHjAcBgNVBAoTFUdvb2dsZSBUcnVzdCBTZXJ2aWNlczET
+MBEGA1UEAxMKR1RTIENBIDFPMTAeFw0xOTEwMTAyMTAyMjhaFw0yMDAxMDIyMTAy
+MjhaMGYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH
+Ew1Nb3VudGFpbiBWaWV3MRMwEQYDVQQKEwpHb29nbGUgTExDMRUwEwYDVQQDDAwq
+Lmdvb2dsZS5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAASQGSFNZeb85+EY
+HSJTKF+w+U3aVOBYE5N2tU4DTzaEfJozQwEdxjjHflOLDowLzMUFQHuc0zGuBN5L
+MEkhPCyCo4IG2zCCBtcwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUF
+BwMBMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFP68VbeQZB5338zH/1Vw2jQ9fyHZ
+MB8GA1UdIwQYMBaAFJjR+G4Q68+b7GCfGJAboOt9Cf0rMGQGCCsGAQUFBwEBBFgw
+VjAnBggrBgEFBQcwAYYbaHR0cDovL29jc3AucGtpLmdvb2cvZ3RzMW8xMCsGCCsG
+AQUFBzAChh9odHRwOi8vcGtpLmdvb2cvZ3NyMi9HVFMxTzEuY3J0MIIEnQYDVR0R
+BIIElDCCBJCCDCouZ29vZ2xlLmNvbYINKi5hbmRyb2lkLmNvbYIWKi5hcHBlbmdp
+bmUuZ29vZ2xlLmNvbYISKi5jbG91ZC5nb29nbGUuY29tghgqLmNyb3dkc291cmNl
+Lmdvb2dsZS5jb22CBiouZy5jb4IOKi5nY3AuZ3Z0Mi5jb22CESouZ2NwY2RuLmd2
+dDEuY29tggoqLmdncGh0LmNugg4qLmdrZWNuYXBwcy5jboIWKi5nb29nbGUtYW5h
+bHl0aWNzLmNvbYILKi5nb29nbGUuY2GCCyouZ29vZ2xlLmNsgg4qLmdvb2dsZS5j
+by5pboIOKi5nb29nbGUuY28uanCCDiouZ29vZ2xlLmNvLnVrgg8qLmdvb2dsZS5j
+b20uYXKCDyouZ29vZ2xlLmNvbS5hdYIPKi5nb29nbGUuY29tLmJygg8qLmdvb2ds
+ZS5jb20uY2+CDyouZ29vZ2xlLmNvbS5teIIPKi5nb29nbGUuY29tLnRygg8qLmdv
+b2dsZS5jb20udm6CCyouZ29vZ2xlLmRlggsqLmdvb2dsZS5lc4ILKi5nb29nbGUu
+ZnKCCyouZ29vZ2xlLmh1ggsqLmdvb2dsZS5pdIILKi5nb29nbGUubmyCCyouZ29v
+Z2xlLnBsggsqLmdvb2dsZS5wdIISKi5nb29nbGVhZGFwaXMuY29tgg8qLmdvb2ds
+ZWFwaXMuY26CESouZ29vZ2xlY25hcHBzLmNughQqLmdvb2dsZWNvbW1lcmNlLmNv
+bYIRKi5nb29nbGV2aWRlby5jb22CDCouZ3N0YXRpYy5jboINKi5nc3RhdGljLmNv
+bYISKi5nc3RhdGljY25hcHBzLmNuggoqLmd2dDEuY29tggoqLmd2dDIuY29tghQq
+Lm1ldHJpYy5nc3RhdGljLmNvbYIMKi51cmNoaW4uY29tghAqLnVybC5nb29nbGUu
+Y29tghMqLndlYXIuZ2tlY25hcHBzLmNughYqLnlvdXR1YmUtbm9jb29raWUuY29t
+gg0qLnlvdXR1YmUuY29tghYqLnlvdXR1YmVlZHVjYXRpb24uY29tghEqLnlvdXR1
+YmVraWRzLmNvbYIHKi55dC5iZYILKi55dGltZy5jb22CGmFuZHJvaWQuY2xpZW50
+cy5nb29nbGUuY29tggthbmRyb2lkLmNvbYIbZGV2ZWxvcGVyLmFuZHJvaWQuZ29v
+Z2xlLmNughxkZXZlbG9wZXJzLmFuZHJvaWQuZ29vZ2xlLmNuggRnLmNvgghnZ3Bo
+dC5jboIMZ2tlY25hcHBzLmNuggZnb28uZ2yCFGdvb2dsZS1hbmFseXRpY3MuY29t
+ggpnb29nbGUuY29tgg9nb29nbGVjbmFwcHMuY26CEmdvb2dsZWNvbW1lcmNlLmNv
+bYIYc291cmNlLmFuZHJvaWQuZ29vZ2xlLmNuggp1cmNoaW4uY29tggp3d3cuZ29v
+Lmdsggh5b3V0dS5iZYILeW91dHViZS5jb22CFHlvdXR1YmVlZHVjYXRpb24uY29t
+gg95b3V0dWJla2lkcy5jb22CBXl0LmJlMCEGA1UdIAQaMBgwCAYGZ4EMAQICMAwG
+CisGAQQB1nkCBQMwLwYDVR0fBCgwJjAkoCKgIIYeaHR0cDovL2NybC5wa2kuZ29v
+Zy9HVFMxTzEuY3JsMIIBBQYKKwYBBAHWeQIEAgSB9gSB8wDxAHcAsh4FzIuizYog
+Todm+Su5iiUgZ2va+nDnsklTLe+LkF4AAAFtt7HklQAABAMASDBGAiEAqQWtUhby
+6kN7bmQ6+HsTWHnsJ6JfetP6BPXd21tzIY8CIQCpj3/wBTW5ak1bJh2yyBaEiYhL
+X2U1QK/l6i1l3AbRhAB2AF6nc/nfVsDntTZIfdBJ4DJ6kZoMhKESEoQYdZaBcUVY
+AAABbbex5KsAAAQDAEcwRQIhAK5DgdFa7XEEqngyBJzkPL11moosB06YVdEG/e2Z
+4t+mAiBdH5bKDIqINpR32vBt8Nqp2L7f8e0jZLsQF/Pj3AP/5zANBgkqhkiG9w0B
+AQsFAAOCAQEAAz/Zkc3geb2WF2T6csWwtFel8aWSXecEWG/xvO0HDlpCPCDUlauI
+8LByL/gimC6Uwc4DJ8hZnr+sSELVo2dZhKhddF5n03VeJNIlOteW4+cFS5Yr2jxG
+vLUtp997vv+rI5p73mWW06GaEJlloHA6M7rfpt6emE6rpX6KESN7mghWUgToyoVw
+hRpGqCyTXvpFCqq9aOkFgPGJBL47NBHq2D7CbYMrooqsNiqZ1CtEWiAMjd2T9Uqz
+DEXc6vVfSEpvdxjKQTqjxnc6grQsBWrVgHU/6+1NBhC5WBqO/INFln2gXuo1CMhr
+Y37udPEQv3QqV2G0uJNcTjYyj1l45W8COA==
+-----END CERTIFICATE-----
+
--- /dev/null
+++ b/test/recipes/90-test_blocklist_data/gts.pem
@@ -0,0 +1,25 @@
+-----BEGIN CERTIFICATE-----
+MIIESjCCAzKgAwIBAgINAeO0mqGNiqmBJWlQuDANBgkqhkiG9w0BAQsFADBMMSAw
+HgYDVQQLExdHbG9iYWxTaWduIFJvb3QgQ0EgLSBSMjETMBEGA1UEChMKR2xvYmFs
+U2lnbjETMBEGA1UEAxMKR2xvYmFsU2lnbjAeFw0xNzA2MTUwMDAwNDJaFw0yMTEy
+MTUwMDAwNDJaMEIxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVHb29nbGUgVHJ1c3Qg
+U2VydmljZXMxEzARBgNVBAMTCkdUUyBDQSAxTzEwggEiMA0GCSqGSIb3DQEBAQUA
+A4IBDwAwggEKAoIBAQDQGM9F1IvN05zkQO9+tN1pIRvJzzyOTHW5DzEZhD2ePCnv
+UA0Qk28FgICfKqC9EksC4T2fWBYk/jCfC3R3VZMdS/dN4ZKCEPZRrAzDsiKUDzRr
+mBBJ5wudgzndIMYcLe/RGGFl5yODIKgjEv/SJH/UL+dEaltN11BmsK+eQmMF++Ac
+xGNhr59qM/9il71I2dN8FGfcddwuaej4bXhp0LcQBbjxMcI7JP0aM3T4I+DsaxmK
+FsbjzaTNC9uzpFlgOIg7rR25xoynUxv8vNmkq7zdPGHXkxWY7oG9j+JkRyBABk7X
+rJfoucBZEqFJJSPk7XA0LKW0Y3z5oz2D0c1tJKwHAgMBAAGjggEzMIIBLzAOBgNV
+HQ8BAf8EBAMCAYYwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMBIGA1Ud
+EwEB/wQIMAYBAf8CAQAwHQYDVR0OBBYEFJjR+G4Q68+b7GCfGJAboOt9Cf0rMB8G
+A1UdIwQYMBaAFJviB1dnHB7AagbeWbSaLd/cGYYuMDUGCCsGAQUFBwEBBCkwJzAl
+BggrBgEFBQcwAYYZaHR0cDovL29jc3AucGtpLmdvb2cvZ3NyMjAyBgNVHR8EKzAp
+MCegJaAjhiFodHRwOi8vY3JsLnBraS5nb29nL2dzcjIvZ3NyMi5jcmwwPwYDVR0g
+BDgwNjA0BgZngQwBAgIwKjAoBggrBgEFBQcCARYcaHR0cHM6Ly9wa2kuZ29vZy9y
+ZXBvc2l0b3J5LzANBgkqhkiG9w0BAQsFAAOCAQEAGoA+Nnn78y6pRjd9XlQWNa7H
+TgiZ/r3RNGkmUmYHPQq6Scti9PEajvwRT2iWTHQr02fesqOqBY2ETUwgZQ+lltoN
+FvhsO9tvBCOIazpswWC9aJ9xju4tWDQH8NVU6YZZ/XteDSGU9YzJqPjY8q3MDxrz
+mqepBCf5o8mw/wJ4a2G6xzUr6Fb6T8McDO22PLRL6u3M4Tzs3A2M1j6bykJYi8wW
+IRdAvKLWZu/axBVbzYmqmwkm5zLSDW5nIAJbELCQCZwMH56t2Dvqofxs6BBcCFIZ
+USpxu6x6td0V7SvJCCosirSmIatj/9dSSVDQibet8q/7UK4v4ZUN80atnZz1yg==
+-----END CERTIFICATE-----
--
2.21.0