Commit d7fd57be authored by Kenton Varda's avatar Kenton Varda Committed by GitHub

Merge pull request #550 from capnproto/tls-client-verification

Implement TLS option to verify client certificates.
parents 2e066e16 3a72273d
......@@ -411,6 +411,70 @@ KJ_TEST("TLS certificate validation") {
"self signed certificate");
}
KJ_TEST("TLS client certificate verification") {
TlsContext::Options serverOptions = TlsTest::defaultServer();
TlsContext::Options clientOptions = TlsTest::defaultClient();
serverOptions.verifyClients = true;
serverOptions.trustedCertificates = clientOptions.trustedCertificates;
// No certificate loaded in the client: fail
{
TlsTest test(clientOptions, serverOptions);
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com");
auto serverPromise = test.tlsServer.wrapServer(kj::mv(pipe.ends[1]));
KJ_EXPECT_THROW_MESSAGE("peer did not return a certificate",
serverPromise.wait(test.io.waitScope));
KJ_EXPECT_THROW_MESSAGE("alert handshake failure",
clientPromise.wait(test.io.waitScope));
}
// Self-signed certificate loaded in the client: fail
{
TlsKeypair selfSignedKeypair = { TlsPrivateKey(HOST_KEY), TlsCertificate(SELF_SIGNED_CERT) };
clientOptions.defaultKeypair = selfSignedKeypair;
TlsTest test(clientOptions, serverOptions);
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com");
auto serverPromise = test.tlsServer.wrapServer(kj::mv(pipe.ends[1]));
KJ_EXPECT_THROW_MESSAGE("certificate verify failed",
serverPromise.wait(test.io.waitScope));
KJ_EXPECT_THROW_MESSAGE("alert unknown ca",
clientPromise.wait(test.io.waitScope));
}
// Trusted certificate loaded in the client: success.
{
clientOptions.defaultKeypair = serverOptions.defaultKeypair;
TlsTest test(clientOptions, serverOptions);
ErrorNexus e;
auto pipe = test.io.provider->newTwoWayPipe();
auto clientPromise = e.wrap(test.tlsClient.wrapClient(kj::mv(pipe.ends[0]), "example.com"));
auto serverPromise = e.wrap(test.tlsServer.wrapServer(kj::mv(pipe.ends[1])));
auto client = clientPromise.wait(test.io.waitScope);
auto server = serverPromise.wait(test.io.waitScope);
auto writePromise = client->write("foo", 3);
char buf[4];
server->read(&buf, 3).wait(test.io.waitScope);
buf[3] = '\0';
KJ_ASSERT(kj::StringPtr(buf) == "foo");
}
}
#ifdef KJ_EXTERNAL_TESTS
KJ_TEST("TLS to capnproto.org") {
kj::AsyncIoContext io = setupAsyncIo();
......
......@@ -473,6 +473,7 @@ private:
TlsContext::Options::Options()
: useSystemTrustStore(true),
verifyClients(false),
minVersion(TlsVersion::TLS_1_0),
cipherList("ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS") {}
// Cipher list is Mozilla's "intermediate" list, except with classic DH removed since we don't
......@@ -516,6 +517,10 @@ TlsContext::TlsContext(Options options) {
}
}
if (options.verifyClients) {
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
}
// honor options.minVersion
long optionFlags = 0;
if (options.minVersion > TlsVersion::SSL_3) {
......
......@@ -58,6 +58,13 @@ public:
bool useSystemTrustStore;
// Whether or not to trust the system's default trust store. Default: true.
bool verifyClients;
// If true, when acting as a server, require the client to present a certificate. The
// certificate must be signed by one of the trusted CAs, otherwise the client will be rejected.
// (Typically you should set `useSystemTrustStore` false when using this flag, and specify
// your specific trusted CAs in `trustedCertificates`.)
// Default: false
kj::ArrayPtr<const TlsCertificate> trustedCertificates;
// Additional certificates which should be trusted. Default: none.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment