Commit d9d30843 authored by Sermet Iskin's avatar Sermet Iskin Committed by Facebook Github Bot

Add peer name auth (i.e. server auth) support to AsyncSSLSocket through SSLContext

Summary:
This change adds basic support for server auth through AsyncSSLSocket and fills in the implementation under SSLContext::authenticate() to support peer name verification.

virtual void SSLContext::authenticate(
      bool checkPeerCert,
      bool checkPeerName,
      const std::string& peerName = std::string());

SSLContext::authenticate signature has parameters intended for server-auth scenarios, but it doesn't have an implementation underneath it and silently ignores the checkPeerName and peerName parameters. This change implements the intended behavior through OPENSS's SSL_set1_host. The intended behavior is as follows:
* When checkPeerName is false, the behavior is unchanged and this should cover all existing use cases.
* When checkPeerName is true, either the peerName or the servername (suppled through the AsyncSSLSocket constructor) in this given order is expected to be present in the SAN or CN of the peer's certificate as described in [the OPENSSL documentation](https://www.openssl.org/docs/man1.1.0/man3/SSL_add1_host.html). Socket will error our if neither name is supplied. Wildcard matching when checking the name is intentionally disabled for now since there is no known need for it at the moment.

Unit tests are added to cover both positive and negative scenarios around SSL server name auth.

Reviewed By: mingtaoy

Differential Revision: D18162775

fbshipit-source-id: d275f5e2e9e760895c27ba9d73cf8b6a2d0ff599
parent f4fb4266
......@@ -718,10 +718,22 @@ bool AsyncSSLSocket::needsPeerVerification() const {
verifyPeer_ == SSLContext::SSLVerifyPeerEnum::VERIFY_REQ_CLIENT_CERT);
}
void AsyncSSLSocket::applyVerificationOptions(const ssl::SSLUniquePtr& ssl) {
bool AsyncSSLSocket::applyVerificationOptions(const ssl::SSLUniquePtr& ssl) {
// apply the settings specified in verifyPeer_
if (verifyPeer_ == SSLContext::SSLVerifyPeerEnum::USE_CTX) {
if (ctx_->needsPeerVerification()) {
if (ctx_->checkPeerName()) {
std::string peerNameToVerify = !ctx_->peerFixedName().empty()
? ctx_->peerFixedName()
: tlsextHostname_;
X509_VERIFY_PARAM* param = SSL_get0_param(ssl.get());
if (!X509_VERIFY_PARAM_set1_host(
param, peerNameToVerify.c_str(), peerNameToVerify.length())) {
return false;
}
}
SSL_set_verify(
ssl.get(),
ctx_->getVerificationMode(),
......@@ -736,6 +748,8 @@ void AsyncSSLSocket::applyVerificationOptions(const ssl::SSLUniquePtr& ssl) {
AsyncSSLSocket::sslVerifyCallback);
}
}
return true;
}
bool AsyncSSLSocket::setupSSLBio() {
......@@ -795,7 +809,13 @@ void AsyncSSLSocket::sslConn(
return failHandshake(__func__, *ex);
}
applyVerificationOptions(ssl_);
if (!applyVerificationOptions(ssl_)) {
sslState_ = STATE_ERROR;
static const Indestructible<AsyncSocketException> ex(
AsyncSocketException::INTERNAL_ERROR,
"error applying the SSL verification options");
return failHandshake(__func__, *ex);
}
if (sslSession_ != nullptr) {
sessionResumptionAttempted_ = true;
......@@ -1125,7 +1145,13 @@ void AsyncSSLSocket::handleAccept() noexcept {
SSL_set_ex_data(ssl_.get(), getSSLExDataIndex(), this);
applyVerificationOptions(ssl_);
if (!applyVerificationOptions(ssl_)) {
sslState_ = STATE_ERROR;
static const Indestructible<AsyncSocketException> ex(
AsyncSocketException::INTERNAL_ERROR,
"error applying the SSL verification options");
return failHandshake(__func__, *ex);
}
}
if (server_ && parseClientHello_) {
......@@ -1761,6 +1787,7 @@ int AsyncSSLSocket::sslVerifyCallback(
VLOG(3) << "AsyncSSLSocket::sslVerifyCallback() this=" << self << ", "
<< "fd=" << self->fd_ << ", preverifyOk=" << preverifyOk;
return (self->handshakeCallback_)
? self->handshakeCallback_->handshakeVer(self, preverifyOk, x509Ctx)
: preverifyOk;
......
......@@ -839,7 +839,7 @@ class AsyncSSLSocket : public virtual AsyncSocket {
* applied. If verifyPeer_ was explicitly set either via sslConn/sslAccept,
* those options override the settings in the underlying SSLContext.
*/
void applyVerificationOptions(const ssl::SSLUniquePtr& ssl);
bool applyVerificationOptions(const ssl::SSLUniquePtr& ssl);
/**
* Sets up SSL with a custom write bio which intercepts all writes.
......
......@@ -45,6 +45,7 @@ void getctx(
std::shared_ptr<folly::SSLContext> clientCtx,
std::shared_ptr<folly::SSLContext> serverCtx) {
clientCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
clientCtx->loadTrustedCertificates(kTestCA);
serverCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
serverCtx->loadCertificate(kTestCert);
......@@ -185,4 +186,70 @@ TEST_F(SSLSessionTest, GetSessionID) {
auto sessID = sess->getSessionID();
ASSERT_GE(sessID.length(), 0);
}
TEST_F(SSLSessionTest, ServerAuthWithPeerName) {
NetworkSocket fds[2];
getfds(fds);
clientCtx->authenticate(true, true, std::string(kTestCertCN));
clientCtx->setVerificationOption(
folly::SSLContext::SSLVerifyPeerEnum::VERIFY);
AsyncSSLSocket::UniquePtr clientSock(
new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
auto clientPtr = clientSock.get();
AsyncSSLSocket::UniquePtr serverSock(
new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
SSLHandshakeClient client(std::move(clientSock), true, true);
SSLHandshakeServerParseClientHello server(
std::move(serverSock), false, false);
eventBase.loop();
ASSERT_TRUE(client.handshakeVerify_);
ASSERT_TRUE(client.handshakeSuccess_);
ASSERT_FALSE(client.handshakeError_);
std::unique_ptr<SSLSession> sess =
std::make_unique<SSLSession>(clientPtr->getSSLSession());
ASSERT_NE(sess, nullptr);
}
TEST_F(SSLSessionTest, ServerAuth_MismatchedPeerName) {
NetworkSocket fds[2];
getfds(fds);
clientCtx->authenticate(true, true, "foo");
clientCtx->setVerificationOption(
folly::SSLContext::SSLVerifyPeerEnum::VERIFY);
AsyncSSLSocket::UniquePtr clientSock(
new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
AsyncSSLSocket::UniquePtr serverSock(
new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
SSLHandshakeClient client(std::move(clientSock), false, false);
SSLHandshakeServerParseClientHello server(
std::move(serverSock), false, false);
eventBase.loop();
ASSERT_TRUE(client.handshakeVerify_);
ASSERT_FALSE(client.handshakeSuccess_);
ASSERT_TRUE(client.handshakeError_);
}
TEST_F(SSLSessionTest, ServerAuth_MismatchedServerName) {
NetworkSocket fds[2];
getfds(fds);
clientCtx->authenticate(true, true);
clientCtx->setVerificationOption(
folly::SSLContext::SSLVerifyPeerEnum::VERIFY);
AsyncSSLSocket::UniquePtr clientSock(
new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
AsyncSSLSocket::UniquePtr serverSock(
new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
SSLHandshakeClient client(std::move(clientSock), false, false);
SSLHandshakeServerParseClientHello server(
std::move(serverSock), false, false);
eventBase.loop();
ASSERT_TRUE(client.handshakeVerify_);
ASSERT_FALSE(client.handshakeSuccess_);
ASSERT_TRUE(client.handshakeError_);
}
} // namespace folly
......@@ -21,6 +21,7 @@ namespace folly {
const char* kTestCert = "folly/io/async/test/certs/tests-cert.pem";
const char* kTestKey = "folly/io/async/test/certs/tests-key.pem";
const char* kTestCA = "folly/io/async/test/certs/ca-cert.pem";
const char* kTestCertCN = "Asox Company";
const char* kClientTestCert = "folly/io/async/test/certs/client_cert.pem";
const char* kClientTestKey = "folly/io/async/test/certs/client_key.pem";
......
......@@ -38,6 +38,7 @@ namespace folly {
extern const char* kTestCert;
extern const char* kTestKey;
extern const char* kTestCA;
extern const char* kTestCertCN;
extern const char* kClientTestCert;
extern const char* kClientTestKey;
......
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