From fbdf69e683faf90b91dedf86e7198a1dc3fb8dc6 Mon Sep 17 00:00:00 2001 From: Dave Watson Date: Mon, 29 Dec 2014 09:58:29 -0800 Subject: [PATCH] Kill fbthrift dep Summary: (almost) all the necessary code has been moved to folly. A couple last functions in SSLUtils, moved them to SSLContext I think this is enough so that open source mcrouter doesn't need an fbthrift dep anymore - although stuff in mcrouter/facebook still uses thrift. Test Plan: fbconfig -r mcrouter; fbmake runtests_dev contbuild will probably show some other projects to fix up for SSLUtils Reviewed By: alikhtarov@fb.com Subscribers: doug, ps, alandau, bmatheny, alikhtarov, mshneer, folly-diffs@ FB internal diff: D1747133 Signature: t1:1747133:1418946064:1c30a60e43f017213e5514d462f267f91761abbe --- folly/io/async/SSLContext.cpp | 79 +++++++++++++++++++++++++++++++++++ folly/io/async/SSLContext.h | 39 +++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/folly/io/async/SSLContext.cpp b/folly/io/async/SSLContext.cpp index 02eba8f8..1b0018fd 100644 --- a/folly/io/async/SSLContext.cpp +++ b/folly/io/async/SSLContext.cpp @@ -690,4 +690,83 @@ 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 df3e9e04..fb7eafac 100644 --- a/folly/io/async/SSLContext.h +++ b/folly/io/async/SSLContext.h @@ -26,6 +26,9 @@ #include #include +#include +#include + #include namespace folly { @@ -462,4 +465,40 @@ 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 -- 2.34.1