blob: 64837d2bf1a3ad398e75363aea31f37ac3e793fb [file] [log] [blame]
// Copyright 2019 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package security
import (
"context"
"fmt"
"path/filepath"
"strconv"
"golang.org/x/sys/unix"
"chromiumos/tast/common/testexec"
"chromiumos/tast/errors"
"chromiumos/tast/testing"
)
func init() {
testing.AddTest(&testing.Test{
Func: OpenSSLBlocklist,
Desc: "Verifies that OpenSSL certificate blocklisting works",
Contacts: []string{
"jorgelo@chromium.org", // Security team
"chromeos-security@google.com",
},
Data: []string{
"openssl_blocklist_ca.pem",
"openssl_blocklist_cert.key",
"openssl_blocklist_cert.pem",
"openssl_blocklist_bogus_blocklist",
"openssl_blocklist_serial_blocklist",
"openssl_blocklist_sha1_blocklist",
"openssl_blocklist_sha256_blocklist",
},
Attr: []string{"group:mainline"},
})
}
func OpenSSLBlocklist(ctx context.Context, s *testing.State) {
const (
blocklistPath = "/etc/ssl/blocklist"
nullBlocklist = "/dev/null"
)
var (
caPEM = s.DataPath("openssl_blocklist_ca.pem")
certKey = s.DataPath("openssl_blocklist_cert.key")
certPEM = s.DataPath("openssl_blocklist_cert.pem")
bogusBlocklist = s.DataPath("openssl_blocklist_bogus_blocklist")
)
blocklists := []string{
s.DataPath("openssl_blocklist_serial_blocklist"),
s.DataPath("openssl_blocklist_sha1_blocklist"),
s.DataPath("openssl_blocklist_sha256_blocklist"),
}
// verify runs "openssl verify" against the cert while using the supplied blocklist.
verify := func(blocklist string, dumpOnFail bool) error {
if err := unix.Mount(blocklist, blocklistPath, "none", unix.MS_BIND, ""); err != nil {
return errors.Wrapf(err, "failed to bind mount %q on %q", blocklist, blocklistPath)
}
defer unix.Unmount(blocklistPath, unix.MNT_DETACH)
cmd := testexec.CommandContext(ctx, "openssl", "verify", "-CAfile", caPEM, certPEM)
err := cmd.Run()
if err != nil && dumpOnFail {
cmd.DumpLog(ctx)
}
return err
}
s.Log("Verifying blocklists")
if err := verify(nullBlocklist, true); err != nil {
s.Fatal("Cert does not verify normally: ", err)
}
if err := verify(bogusBlocklist, true); err != nil {
s.Fatal("Cert does not verify with non-empty blocklist: ", err)
}
for _, bl := range blocklists {
if err := verify(bl, false); err == nil {
s.Error("Cert unexpectedly verified with ", filepath.Base(bl))
}
}
const port = 4433
s.Log("Starting openssl s_server on port ", port)
srvCmd := testexec.CommandContext(ctx, "openssl", "s_server", "-www",
"-CAfile", caPEM, "-cert", certPEM, "-key", certKey, "-port", strconv.Itoa(port))
if err := srvCmd.Start(); err != nil {
defer srvCmd.DumpLog(ctx)
s.Fatal("Failed to start openssl server: ", err)
}
defer func() {
srvCmd.Kill()
srvCmd.Wait()
}()
// fetch uses curl with the blocklist at the supplied path to connect to the server.
fetch := func(ctx context.Context, blocklist string) error {
if err := unix.Mount(blocklist, blocklistPath, "none", unix.MS_BIND, ""); err != nil {
return errors.Wrapf(err, "failed to bind mount %q on %q", blocklist, blocklistPath)
}
defer unix.Unmount(blocklistPath, unix.MNT_DETACH)
cmd := testexec.CommandContext(ctx, "curl", "--cacert", caPEM,
fmt.Sprintf("https://127.0.0.1:%d/", port), "-o", "/dev/null")
return cmd.Run()
}
s.Log("Waiting for server to be ready")
if err := testing.Poll(ctx, func(ctx context.Context) error {
return fetch(ctx, nullBlocklist)
}, nil); err != nil {
s.Fatal("Failed waiting for server to be ready: ", err)
}
for _, bl := range blocklists {
s.Log("Connecting to server using ", filepath.Base(bl))
if err := fetch(ctx, bl); err == nil {
s.Error("Connection unexpectedly succeeded using ", filepath.Base(bl))
}
}
}