From 4700a31c198bf9d5a6bb58eb24fd97941f59d7ec Mon Sep 17 00:00:00 2001 From: Anirudh Ramachandran Date: Tue, 8 Mar 2016 18:09:22 -0800 Subject: [PATCH] SSL cleanup: moving some OpenSSL definitions to new dir folly/io/async/ssl Summary:SSLContext and AsyncSSLSocket are growing with a lot of code that is OpenSSL-specific, and it may be good to refactor some of these before it gets out of hand. This is also useful to reduce complexity as we some additional features such as ServerHello parsing and TLS Cached Info (D2936570) Main changes: * Created a subdirectory folly/io/async/ssl to refactor code from folly/io/async. We may want to consider moving this out of folly/io/async * Moved OpenSSLPtrTypes.h to folly/io/async/ssl/OpenSSLPtrTypes.h * Moved 'OpenSSLUtils' from SSLContext to separate file OpenSSLUtils.{h,cpp} * Moved TLSExtensions and ClientHelloInfo from AsyncSSLSocket to TLSDefinitions.h Reviewed By: siyengar Differential Revision: D2978707 fb-gh-sync-id: a21f02947aeffccc447da2124a91cc99315df1c7 shipit-source-id: a21f02947aeffccc447da2124a91cc99315df1c7 --- folly/Makefile.am | 5 +- folly/io/async/AsyncSSLSocket.cpp | 16 +-- folly/io/async/AsyncSSLSocket.h | 74 ++------------ folly/io/async/AsyncTransport.h | 6 +- folly/io/async/OpenSSLPtrTypes.h | 33 ------- folly/io/async/SSLContext.cpp | 92 +----------------- folly/io/async/SSLContext.h | 37 +------ folly/io/async/ssl/OpenSSLPtrTypes.h | 78 +++++++++++++++ folly/io/async/ssl/OpenSSLUtils.cpp | 108 +++++++++++++++++++++ folly/io/async/ssl/OpenSSLUtils.h | 61 ++++++++++++ folly/io/async/ssl/TLSDefinitions.h | 92 ++++++++++++++++++ folly/io/async/test/AsyncSSLSocketTest.cpp | 13 +-- 12 files changed, 374 insertions(+), 241 deletions(-) delete mode 100644 folly/io/async/OpenSSLPtrTypes.h create mode 100644 folly/io/async/ssl/OpenSSLPtrTypes.h create mode 100644 folly/io/async/ssl/OpenSSLUtils.cpp create mode 100644 folly/io/async/ssl/OpenSSLUtils.h create mode 100644 folly/io/async/ssl/TLSDefinitions.h diff --git a/folly/Makefile.am b/folly/Makefile.am index 70de07e0..3f443eec 100644 --- a/folly/Makefile.am +++ b/folly/Makefile.am @@ -229,7 +229,9 @@ nobase_follyinclude_HEADERS = \ io/async/EventUtil.h \ io/async/NotificationQueue.h \ io/async/HHWheelTimer.h \ - io/async/OpenSSLPtrTypes.h \ + io/async/ssl/OpenSSLPtrTypes.h \ + io/async/ssl/OpenSSLUtils.h \ + io/async/ssl/TLSDefinitions.h \ io/async/Request.h \ io/async/SSLContext.h \ io/async/ScopedEventBaseThread.h \ @@ -397,6 +399,7 @@ libfolly_la_SOURCES = \ io/async/HHWheelTimer.cpp \ io/async/test/SocketPair.cpp \ io/async/test/TimeUtil.cpp \ + io/async/ssl/OpenSSLUtils.cpp \ json.cpp \ detail/MemoryIdler.cpp \ MacAddress.cpp \ diff --git a/folly/io/async/AsyncSSLSocket.cpp b/folly/io/async/AsyncSSLSocket.cpp index a0224877..97cce2d9 100644 --- a/folly/io/async/AsyncSSLSocket.cpp +++ b/folly/io/async/AsyncSSLSocket.cpp @@ -1634,7 +1634,7 @@ int AsyncSSLSocket::sslVerifyCallback(int preverifyOk, void AsyncSSLSocket::enableClientHelloParsing() { parseClientHello_ = true; - clientHelloInfo_.reset(new ClientHelloInfo()); + clientHelloInfo_.reset(new ssl::ClientHelloInfo()); } void AsyncSSLSocket::resetClientHelloParsing(SSL *ssl) { @@ -1711,22 +1711,22 @@ void AsyncSSLSocket::clientHelloParsingCallback(int written, if (cursor.totalLength() > 0) { uint16_t extensionsLength = cursor.readBE(); while (extensionsLength) { - TLSExtension extensionType = static_cast( - cursor.readBE()); + ssl::TLSExtension extensionType = + static_cast(cursor.readBE()); sock->clientHelloInfo_-> clientHelloExtensions_.push_back(extensionType); extensionsLength -= 2; uint16_t extensionDataLength = cursor.readBE(); extensionsLength -= 2; - if (extensionType == TLSExtension::SIGNATURE_ALGORITHMS) { + if (extensionType == ssl::TLSExtension::SIGNATURE_ALGORITHMS) { cursor.skip(2); extensionDataLength -= 2; while (extensionDataLength) { - HashAlgorithm hashAlg = static_cast( - cursor.readBE()); - SignatureAlgorithm sigAlg = static_cast( - cursor.readBE()); + ssl::HashAlgorithm hashAlg = + static_cast(cursor.readBE()); + ssl::SignatureAlgorithm sigAlg = + static_cast(cursor.readBE()); extensionDataLength -= 2; sock->clientHelloInfo_-> clientHelloSigAlgs_.emplace_back(hashAlg, sigAlg); diff --git a/folly/io/async/AsyncSSLSocket.h b/folly/io/async/AsyncSSLSocket.h index 4ab04860..2e4d5bac 100644 --- a/folly/io/async/AsyncSSLSocket.h +++ b/folly/io/async/AsyncSSLSocket.h @@ -22,10 +22,12 @@ #include #include #include -#include #include -#include +#include #include +#include +#include +#include #include #include @@ -652,68 +654,8 @@ class AsyncSSLSocket : public virtual AsyncSocket { int content_type, const void *buf, size_t len, SSL *ssl, void *arg); static const char* getSSLServerNameFromSSL(SSL* ssl); - // http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml - enum class TLSExtension: uint16_t { - SERVER_NAME = 0, - MAX_FRAGMENT_LENGTH = 1, - CLIENT_CERTIFICATE_URL = 2, - TRUSTED_CA_KEYS = 3, - TRUNCATED_HMAC = 4, - STATUS_REQUEST = 5, - USER_MAPPING = 6, - CLIENT_AUTHZ = 7, - SERVER_AUTHZ = 8, - CERT_TYPE = 9, - SUPPORTED_GROUPS = 10, - EC_POINT_FORMATS = 11, - SRP = 12, - SIGNATURE_ALGORITHMS = 13, - USE_SRTP = 14, - HEARTBEAT = 15, - APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16, - STATUS_REQUEST_V2 = 17, - SIGNED_CERTIFICATE_TIMESTAMP = 18, - CLIENT_CERTIFICATE_TYPE = 19, - SERVER_CERTIFICATE_TYPE = 20, - PADDING = 21, - ENCRYPT_THEN_MAC = 22, - EXTENDED_MASTER_SECRET = 23, - SESSION_TICKET = 35, - RENEGOTIATION_INFO = 65281 - }; - - // http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18 - enum class HashAlgorithm: uint8_t { - NONE = 0, - MD5 = 1, - SHA1 = 2, - SHA224 = 3, - SHA256 = 4, - SHA384 = 5, - SHA512 = 6 - }; - - // http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16 - enum class SignatureAlgorithm: uint8_t { - ANONYMOUS = 0, - RSA = 1, - DSA = 2, - ECDSA = 3 - }; - - struct ClientHelloInfo { - folly::IOBufQueue clientHelloBuf_; - uint8_t clientHelloMajorVersion_; - uint8_t clientHelloMinorVersion_; - std::vector clientHelloCipherSuites_; - std::vector clientHelloCompressionMethods_; - std::vector clientHelloExtensions_; - std::vector< - std::pair> clientHelloSigAlgs_; - }; - // For unit-tests - ClientHelloInfo* getClientHelloInfo() const { + ssl::ClientHelloInfo* getClientHelloInfo() const { return clientHelloInfo_.get(); } @@ -737,13 +679,13 @@ class AsyncSSLSocket : public virtual AsyncSocket { /** * Returns the peer certificate, or nullptr if no peer certificate received. */ - virtual X509_UniquePtr getPeerCert() const override { + virtual ssl::X509UniquePtr getPeerCert() const override { if (!ssl_) { return nullptr; } X509* cert = SSL_get_peer_certificate(ssl_); - return X509_UniquePtr(cert); + return ssl::X509UniquePtr(cert); } /** @@ -886,7 +828,7 @@ class AsyncSSLSocket : public virtual AsyncSocket { bool parseClientHello_{false}; bool cacheAddrOnFailure_{false}; - std::unique_ptr clientHelloInfo_; + std::unique_ptr clientHelloInfo_; // Time taken to complete the ssl handshake. std::chrono::steady_clock::time_point handshakeStartTime_; diff --git a/folly/io/async/AsyncTransport.h b/folly/io/async/AsyncTransport.h index 08ba3284..07e67833 100644 --- a/folly/io/async/AsyncTransport.h +++ b/folly/io/async/AsyncTransport.h @@ -20,10 +20,10 @@ #include #include +#include #include #include -#include -#include +#include #include @@ -324,7 +324,7 @@ class AsyncTransport : public DelayedDestruction, public AsyncSocketBase { /** * Get the certificate used to authenticate the peer. */ - virtual X509_UniquePtr getPeerCert() const { return nullptr; } + virtual ssl::X509UniquePtr getPeerCert() const { return nullptr; } /** * @return True iff end of record tracking is enabled diff --git a/folly/io/async/OpenSSLPtrTypes.h b/folly/io/async/OpenSSLPtrTypes.h deleted file mode 100644 index 57d73b8e..00000000 --- a/folly/io/async/OpenSSLPtrTypes.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2016 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace folly { - -using X509_deleter = static_function_deleter; -using X509_UniquePtr = std::unique_ptr; - -using EVP_PKEY_deleter = - folly::static_function_deleter; -using EVP_PKEY_UniquePtr = std::unique_ptr; - -using SSL_deleter = folly::static_function_deleter; -using SSL_UniquePtr = std::unique_ptr; -} diff --git a/folly/io/async/SSLContext.cpp b/folly/io/async/SSLContext.cpp index 637a0e1f..4706ac6d 100644 --- a/folly/io/async/SSLContext.cpp +++ b/folly/io/async/SSLContext.cpp @@ -24,7 +24,6 @@ #include #include #include -#include // --------------------------------------------------------------------- // SSLContext implementation @@ -45,9 +44,6 @@ std::mutex& initMutex() { return m; } -inline void BIO_free_fb(BIO* bio) { CHECK_EQ(1, BIO_free(bio)); } -using BIO_deleter = folly::static_function_deleter; - } // anonymous namespace #ifdef OPENSSL_NPN_NEGOTIATED @@ -196,7 +192,7 @@ void SSLContext::loadCertificateFromBufferPEM(folly::StringPiece cert) { throw std::invalid_argument("loadCertificate: is nullptr"); } - std::unique_ptr bio(BIO_new(BIO_s_mem())); + ssl::BioUniquePtr bio(BIO_new(BIO_s_mem())); if (bio == nullptr) { throw std::runtime_error("BIO_new: " + getErrors()); } @@ -206,7 +202,8 @@ void SSLContext::loadCertificateFromBufferPEM(folly::StringPiece cert) { throw std::runtime_error("BIO_write: " + getErrors()); } - X509_UniquePtr x509(PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); + ssl::X509UniquePtr x509( + PEM_read_bio_X509(bio.get(), nullptr, nullptr, nullptr)); if (x509 == nullptr) { throw std::runtime_error("PEM_read_bio_X509: " + getErrors()); } @@ -235,7 +232,7 @@ void SSLContext::loadPrivateKeyFromBufferPEM(folly::StringPiece pkey) { throw std::invalid_argument("loadPrivateKey: is nullptr"); } - std::unique_ptr bio(BIO_new(BIO_s_mem())); + ssl::BioUniquePtr bio(BIO_new(BIO_s_mem())); if (bio == nullptr) { throw std::runtime_error("BIO_new: " + getErrors()); } @@ -245,7 +242,7 @@ void SSLContext::loadPrivateKeyFromBufferPEM(folly::StringPiece pkey) { throw std::runtime_error("BIO_write: " + getErrors()); } - EVP_PKEY_UniquePtr key( + ssl::EvpPkeyUniquePtr key( PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr, nullptr)); if (key == nullptr) { throw std::runtime_error("PEM_read_bio_PrivateKey: " + getErrors()); @@ -799,83 +796,4 @@ operator<<(std::ostream& os, const PasswordCollector& collector) { return os; } -bool OpenSSLUtils::getPeerAddressFromX509StoreCtx(X509_STORE_CTX* ctx, - sockaddr_storage* addrStorage, - socklen_t* addrLen) { - // Grab the ssl idx and then the ssl object so that we can get the peer - // name to compare against the ips in the subjectAltName - auto sslIdx = SSL_get_ex_data_X509_STORE_CTX_idx(); - auto ssl = - reinterpret_cast(X509_STORE_CTX_get_ex_data(ctx, sslIdx)); - int fd = SSL_get_fd(ssl); - if (fd < 0) { - LOG(ERROR) << "Inexplicably couldn't get fd from SSL"; - return false; - } - - *addrLen = sizeof(*addrStorage); - if (getpeername(fd, reinterpret_cast(addrStorage), addrLen) != 0) { - PLOG(ERROR) << "Unable to get peer name"; - return false; - } - CHECK(*addrLen <= sizeof(*addrStorage)); - return true; -} - -bool OpenSSLUtils::validatePeerCertNames(X509* cert, - const sockaddr* addr, - socklen_t /* addrLen */) { - // Try to extract the names within the SAN extension from the certificate - auto altNames = - reinterpret_cast( - X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); - SCOPE_EXIT { - if (altNames != nullptr) { - sk_GENERAL_NAME_pop_free(altNames, GENERAL_NAME_free); - } - }; - if (altNames == nullptr) { - LOG(WARNING) << "No subjectAltName provided and we only support ip auth"; - return false; - } - - const sockaddr_in* addr4 = nullptr; - const sockaddr_in6* addr6 = nullptr; - if (addr != nullptr) { - if (addr->sa_family == AF_INET) { - addr4 = reinterpret_cast(addr); - } else if (addr->sa_family == AF_INET6) { - addr6 = reinterpret_cast(addr); - } else { - LOG(FATAL) << "Unsupported sockaddr family: " << addr->sa_family; - } - } - - - for (int i = 0; i < sk_GENERAL_NAME_num(altNames); i++) { - auto name = sk_GENERAL_NAME_value(altNames, i); - if ((addr4 != nullptr || addr6 != nullptr) && name->type == GEN_IPADD) { - // Extra const-ness for paranoia - unsigned char const * const rawIpStr = name->d.iPAddress->data; - int const rawIpLen = name->d.iPAddress->length; - - if (rawIpLen == 4 && addr4 != nullptr) { - if (::memcmp(rawIpStr, &addr4->sin_addr, rawIpLen) == 0) { - return true; - } - } else if (rawIpLen == 16 && addr6 != nullptr) { - if (::memcmp(rawIpStr, &addr6->sin6_addr, rawIpLen) == 0) { - return true; - } - } else if (rawIpLen != 4 && rawIpLen != 16) { - LOG(WARNING) << "Unexpected IP length: " << rawIpLen; - } - } - } - - LOG(WARNING) << "Unable to match client cert against alt name ip"; - return false; -} - - } // folly diff --git a/folly/io/async/SSLContext.h b/folly/io/async/SSLContext.h index 03d41758..d63031a2 100644 --- a/folly/io/async/SSLContext.h +++ b/folly/io/async/SSLContext.h @@ -38,6 +38,8 @@ #include #include +#include +#include namespace folly { @@ -549,40 +551,5 @@ typedef std::shared_ptr SSLContextPtr; std::ostream& operator<<(std::ostream& os, const folly::PasswordCollector& collector); -class OpenSSLUtils { - public: - /** - * Validate that the peer certificate's common name or subject alt names - * match what we expect. Currently this only checks for IPs within - * subject alt names but it could easily be expanded to check common name - * and hostnames as well. - * - * @param cert X509* peer certificate - * @param addr sockaddr object containing sockaddr to verify - * @param addrLen length of sockaddr as returned by getpeername or accept - * @return true iff a subject altname IP matches addr - */ - // TODO(agartrell): Add support for things like common name when - // necessary. - static bool validatePeerCertNames(X509* cert, - const sockaddr* addr, - socklen_t addrLen); - - /** - * Get the peer socket address from an X509_STORE_CTX*. Unlike the - * accept, getsockname, getpeername, etc family of operations, addrLen's - * initial value is ignored and reset. - * - * @param ctx Context from which to retrieve peer sockaddr - * @param addrStorage out param for address - * @param addrLen out param for length of address - * @return true on success, false on failure - */ - static bool getPeerAddressFromX509StoreCtx(X509_STORE_CTX* ctx, - sockaddr_storage* addrStorage, - socklen_t* addrLen); - -}; - } // folly diff --git a/folly/io/async/ssl/OpenSSLPtrTypes.h b/folly/io/async/ssl/OpenSSLPtrTypes.h new file mode 100644 index 00000000..4cee3320 --- /dev/null +++ b/folly/io/async/ssl/OpenSSLPtrTypes.h @@ -0,0 +1,78 @@ +/* + * Copyright 2016 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#ifndef OPENSSL_NO_EC +#include +#endif +#include +#include +#include + +namespace folly { +namespace ssl { + +// X509 +using X509Deleter = folly::static_function_deleter; +using X509UniquePtr = std::unique_ptr; +using X509StoreCtxDeleter = + folly::static_function_deleter; +using X509StoreCtxUniquePtr = + std::unique_ptr; + +// EVP +using EvpPkeyDel = folly::static_function_deleter; +using EvpPkeyUniquePtr = std::unique_ptr; +using EvpPkeySharedPtr = std::shared_ptr; + +// No EVP_PKEY_CTX <= 0.9.8b +#if OPENSSL_VERSION_NUMBER >= 0x10000002L +using EvpPkeyCtxDeleter = + folly::static_function_deleter; +using EvpPkeyCtxUniquePtr = std::unique_ptr; +#else +struct EVP_PKEY_CTX; +#endif +using EvpMdCtxDeleter = + folly::static_function_deleter; +using EvpMdCtxUniquePtr = std::unique_ptr; + +// BIO +using BioDeleter = folly::static_function_deleter; +using BioUniquePtr = std::unique_ptr; +using BioChainDeleter = folly::static_function_deleter; +using BioChainUniquePtr = std::unique_ptr; +inline void BIO_free_fb(BIO* bio) { CHECK_EQ(1, BIO_free(bio)); } +using BioDeleterFb = folly::static_function_deleter; +using BioUniquePtrFb = std::unique_ptr; + +// RSA and EC +using RsaDeleter = folly::static_function_deleter; +using RsaUniquePtr = std::unique_ptr; +#ifndef OPENSSL_NO_EC +using EcKeyDeleter = folly::static_function_deleter; +using EcKeyUniquePtr = std::unique_ptr; +#endif + +// SSL and SSL_CTX +using SSLDeleter = folly::static_function_deleter; +using SSLUniquePtr = std::unique_ptr; +} +} diff --git a/folly/io/async/ssl/OpenSSLUtils.cpp b/folly/io/async/ssl/OpenSSLUtils.cpp new file mode 100644 index 00000000..c276f6d6 --- /dev/null +++ b/folly/io/async/ssl/OpenSSLUtils.cpp @@ -0,0 +1,108 @@ +/* + * Copyright 2016 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#include +#include + +#include +#include +#include +#include + +#include +#include + +#include + +namespace folly { +namespace ssl { + +bool OpenSSLUtils::getPeerAddressFromX509StoreCtx(X509_STORE_CTX* ctx, + sockaddr_storage* addrStorage, + socklen_t* addrLen) { + // Grab the ssl idx and then the ssl object so that we can get the peer + // name to compare against the ips in the subjectAltName + auto sslIdx = SSL_get_ex_data_X509_STORE_CTX_idx(); + auto ssl = reinterpret_cast(X509_STORE_CTX_get_ex_data(ctx, sslIdx)); + int fd = SSL_get_fd(ssl); + if (fd < 0) { + LOG(ERROR) << "Inexplicably couldn't get fd from SSL"; + return false; + } + + *addrLen = sizeof(*addrStorage); + if (getpeername(fd, reinterpret_cast(addrStorage), addrLen) != 0) { + PLOG(ERROR) << "Unable to get peer name"; + return false; + } + CHECK(*addrLen <= sizeof(*addrStorage)); + return true; +} + +bool OpenSSLUtils::validatePeerCertNames(X509* cert, + const sockaddr* addr, + socklen_t /* addrLen */) { + // Try to extract the names within the SAN extension from the certificate + auto altNames = reinterpret_cast( + X509_get_ext_d2i(cert, NID_subject_alt_name, nullptr, nullptr)); + SCOPE_EXIT { + if (altNames != nullptr) { + sk_GENERAL_NAME_pop_free(altNames, GENERAL_NAME_free); + } + }; + if (altNames == nullptr) { + LOG(WARNING) << "No subjectAltName provided and we only support ip auth"; + return false; + } + + const sockaddr_in* addr4 = nullptr; + const sockaddr_in6* addr6 = nullptr; + if (addr != nullptr) { + if (addr->sa_family == AF_INET) { + addr4 = reinterpret_cast(addr); + } else if (addr->sa_family == AF_INET6) { + addr6 = reinterpret_cast(addr); + } else { + LOG(FATAL) << "Unsupported sockaddr family: " << addr->sa_family; + } + } + + for (int i = 0; i < sk_GENERAL_NAME_num(altNames); i++) { + auto name = sk_GENERAL_NAME_value(altNames, i); + if ((addr4 != nullptr || addr6 != nullptr) && name->type == GEN_IPADD) { + // Extra const-ness for paranoia + unsigned char const* const rawIpStr = name->d.iPAddress->data; + int const rawIpLen = name->d.iPAddress->length; + + if (rawIpLen == 4 && addr4 != nullptr) { + if (::memcmp(rawIpStr, &addr4->sin_addr, rawIpLen) == 0) { + return true; + } + } else if (rawIpLen == 16 && addr6 != nullptr) { + if (::memcmp(rawIpStr, &addr6->sin6_addr, rawIpLen) == 0) { + return true; + } + } else if (rawIpLen != 4 && rawIpLen != 16) { + LOG(WARNING) << "Unexpected IP length: " << rawIpLen; + } + } + } + + LOG(WARNING) << "Unable to match client cert against alt name ip"; + return false; +} + +} // ssl +} // folly diff --git a/folly/io/async/ssl/OpenSSLUtils.h b/folly/io/async/ssl/OpenSSLUtils.h new file mode 100644 index 00000000..5d232c44 --- /dev/null +++ b/folly/io/async/ssl/OpenSSLUtils.h @@ -0,0 +1,61 @@ +/* + * Copyright 2016 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#pragma once + +#include + +#include +#include + +namespace folly { +namespace ssl { + +class OpenSSLUtils { + public: + /** + * Validate that the peer certificate's common name or subject alt names + * match what we expect. Currently this only checks for IPs within + * subject alt names but it could easily be expanded to check common name + * and hostnames as well. + * + * @param cert X509* peer certificate + * @param addr sockaddr object containing sockaddr to verify + * @param addrLen length of sockaddr as returned by getpeername or accept + * @return true iff a subject altname IP matches addr + */ + // TODO(agartrell): Add support for things like common name when + // necessary. + static bool validatePeerCertNames(X509* cert, + const sockaddr* addr, + socklen_t addrLen); + + /** + * Get the peer socket address from an X509_STORE_CTX*. Unlike the + * accept, getsockname, getpeername, etc family of operations, addrLen's + * initial value is ignored and reset. + * + * @param ctx Context from which to retrieve peer sockaddr + * @param addrStorage out param for address + * @param addrLen out param for length of address + * @return true on success, false on failure + */ + static bool getPeerAddressFromX509StoreCtx(X509_STORE_CTX* ctx, + sockaddr_storage* addrStorage, + socklen_t* addrLen); +}; + +} // ssl +} // folly diff --git a/folly/io/async/ssl/TLSDefinitions.h b/folly/io/async/ssl/TLSDefinitions.h new file mode 100644 index 00000000..cf803d10 --- /dev/null +++ b/folly/io/async/ssl/TLSDefinitions.h @@ -0,0 +1,92 @@ +/* + * Copyright 2016 Facebook, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace folly { +namespace ssl { + +// http://www.iana.org/assignments/tls-extensiontype-values/tls-extensiontype-values.xhtml +enum class TLSExtension : uint16_t { + SERVER_NAME = 0, + MAX_FRAGMENT_LENGTH = 1, + CLIENT_CERTIFICATE_URL = 2, + TRUSTED_CA_KEYS = 3, + TRUNCATED_HMAC = 4, + STATUS_REQUEST = 5, + USER_MAPPING = 6, + CLIENT_AUTHZ = 7, + SERVER_AUTHZ = 8, + CERT_TYPE = 9, + SUPPORTED_GROUPS = 10, + EC_POINT_FORMATS = 11, + SRP = 12, + SIGNATURE_ALGORITHMS = 13, + USE_SRTP = 14, + HEARTBEAT = 15, + APPLICATION_LAYER_PROTOCOL_NEGOTIATION = 16, + STATUS_REQUEST_V2 = 17, + SIGNED_CERTIFICATE_TIMESTAMP = 18, + CLIENT_CERTIFICATE_TYPE = 19, + SERVER_CERTIFICATE_TYPE = 20, + PADDING = 21, + ENCRYPT_THEN_MAC = 22, + EXTENDED_MASTER_SECRET = 23, + SESSION_TICKET = 35, + // Facebook-specific, not IANA assigned yet + TLS_CACHED_INFO_FB = 60001, + // End Facebook-specific + RENEGOTIATION_INFO = 65281 +}; + +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-18 +enum class HashAlgorithm : uint8_t { + NONE = 0, + MD5 = 1, + SHA1 = 2, + SHA224 = 3, + SHA256 = 4, + SHA384 = 5, + SHA512 = 6 +}; + +// http://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-16 +enum class SignatureAlgorithm : uint8_t { + ANONYMOUS = 0, + RSA = 1, + DSA = 2, + ECDSA = 3 +}; + +struct ClientHelloInfo { + folly::IOBufQueue clientHelloBuf_; + uint8_t clientHelloMajorVersion_; + uint8_t clientHelloMinorVersion_; + std::vector clientHelloCipherSuites_; + std::vector clientHelloCompressionMethods_; + std::vector clientHelloExtensions_; + std::vector> clientHelloSigAlgs_; +}; + +} // ssl +} // folly diff --git a/folly/io/async/test/AsyncSSLSocketTest.cpp b/folly/io/async/test/AsyncSSLSocketTest.cpp index 87909f0e..038f4f31 100644 --- a/folly/io/async/test/AsyncSSLSocketTest.cpp +++ b/folly/io/async/test/AsyncSSLSocketTest.cpp @@ -57,9 +57,6 @@ const char* testCA = "folly/io/async/test/certs/ca-cert.pem"; constexpr size_t SSLClient::kMaxReadBufferSz; constexpr size_t SSLClient::kMaxReadsPerEvent; -inline void BIO_free_fb(BIO* bio) { CHECK_EQ(1, BIO_free(bio)); } -using BIO_deleter = folly::static_function_deleter; - TestSSLServer::TestSSLServer(SSLServerAcceptCallbackBase* acb) : ctx_(new folly::SSLContext), acb_(acb), @@ -1384,15 +1381,15 @@ TEST(AsyncSSLSocketTest, LoadCertFromMemory) { auto cert = getFileAsBuf(testCert); auto key = getFileAsBuf(testKey); - std::unique_ptr certBio(BIO_new(BIO_s_mem())); + ssl::BioUniquePtr certBio(BIO_new(BIO_s_mem())); BIO_write(certBio.get(), cert.data(), cert.size()); - std::unique_ptr keyBio(BIO_new(BIO_s_mem())); + ssl::BioUniquePtr keyBio(BIO_new(BIO_s_mem())); BIO_write(keyBio.get(), key.data(), key.size()); // Create SSL structs from buffers to get properties - X509_UniquePtr certStruct( + ssl::X509UniquePtr certStruct( PEM_read_bio_X509(certBio.get(), nullptr, nullptr, nullptr)); - EVP_PKEY_UniquePtr keyStruct( + ssl::EvpPkeyUniquePtr keyStruct( PEM_read_bio_PrivateKey(keyBio.get(), nullptr, nullptr, nullptr)); certBio = nullptr; keyBio = nullptr; @@ -1407,7 +1404,7 @@ TEST(AsyncSSLSocketTest, LoadCertFromMemory) { ctx->loadCertificateFromBufferPEM(cert); ctx->loadTrustedCertificates(testCA); - SSL_UniquePtr ssl(ctx->createSSL()); + ssl::SSLUniquePtr ssl(ctx->createSSL()); auto newCert = SSL_get_certificate(ssl.get()); auto newKey = SSL_get_privatekey(ssl.get()); -- 2.34.1