SSLContext::ServerNameCallbackResult SSLContextManager::serverNameCallback(SSL* ssl) { shared_ptr<SSLContext> ctx; const char* sn = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (!sn) { VLOG(6) << "Server Name (tlsext_hostname) is missing"; if (clientHelloTLSExtStats_) { clientHelloTLSExtStats_->recordAbsentHostname(); } return SSLContext::SERVER_NAME_NOT_FOUND; } size_t snLen = strlen(sn); VLOG(6) << "Server Name (SNI TLS extension): '" << sn << "' "; // FIXME: This code breaks the abstraction. Suggestion? AsyncSSLSocket* sslSocket = AsyncSSLSocket::getFromSSL(ssl); CHECK(sslSocket); DNString dnstr(sn, snLen); uint32_t count = 0; do { // Try exact match first ctx = getSSLCtx(dnstr); if (ctx) { sslSocket->switchServerSSLContext(ctx); if (clientHelloTLSExtStats_) { clientHelloTLSExtStats_->recordMatch(); } return SSLContext::SERVER_NAME_FOUND; } ctx = getSSLCtxBySuffix(dnstr); if (ctx) { sslSocket->switchServerSSLContext(ctx); if (clientHelloTLSExtStats_) { clientHelloTLSExtStats_->recordMatch(); } return SSLContext::SERVER_NAME_FOUND; } // Give the noMatchFn one chance to add the correct cert } while (count++ == 0 && noMatchFn_ && noMatchFn_(sn)); VLOG(6) << folly::stringPrintf("Cannot find a SSL_CTX for \"%s\"", sn); if (clientHelloTLSExtStats_) { clientHelloTLSExtStats_->recordNotMatch(); } return SSLContext::SERVER_NAME_NOT_FOUND; }
void CurlClient::sslHandshakeFollowup(HTTPUpstreamSession* session) noexcept { AsyncSSLSocket* sslSocket = dynamic_cast<AsyncSSLSocket*>( session->getTransport()); const unsigned char* nextProto = nullptr; unsigned nextProtoLength = 0; sslSocket->getSelectedNextProtocol(&nextProto, &nextProtoLength); if (nextProto) { VLOG(1) << "Client selected next protocol " << string((const char*)nextProto, nextProtoLength); } else { VLOG(1) << "Client did not select a next protocol"; } // Note: This ssl session can be used by defining a member and setting // something like sslSession_ = sslSocket->getSSLSession() and then // passing it to the connector::connectSSL() method }
void HTTPConnector::connectSuccess() noexcept { if (!cb_) { return; } folly::SocketAddress localAddress; folly::SocketAddress peerAddress; socket_->getLocalAddress(&localAddress); socket_->getPeerAddress(&peerAddress); std::unique_ptr<HTTPCodec> codec; transportInfo_.acceptTime = getCurrentTime(); if (transportInfo_.ssl) { AsyncSSLSocket* sslSocket = dynamic_cast<AsyncSSLSocket*>(socket_.get()); const char* npnProto; unsigned npnProtoLen; sslSocket->getSelectedNextProtocol( reinterpret_cast<const unsigned char**>(&npnProto), &npnProtoLen); transportInfo_.sslNextProtocol = std::make_shared<std::string>(npnProto, npnProtoLen); transportInfo_.sslSetupTime = millisecondsSince(connectStart_); transportInfo_.sslCipher = sslSocket->getNegotiatedCipherName() ? std::make_shared<std::string>(sslSocket->getNegotiatedCipherName()) : nullptr; transportInfo_.sslVersion = sslSocket->getSSLVersion(); transportInfo_.sslResume = wangle::SSLUtil::getResumeState(sslSocket); codec = makeCodec(*transportInfo_.sslNextProtocol, forceHTTP1xCodecTo1_1_); } else { codec = makeCodec(plaintextProtocol_, forceHTTP1xCodecTo1_1_); } HTTPUpstreamSession* session = new HTTPUpstreamSession( timeoutSet_, std::move(socket_), localAddress, peerAddress, std::move(codec), transportInfo_, nullptr); cb_->connectSuccess(session); }
void HTTPConnector::connectSuccess() noexcept { if (!cb_) { return; } folly::SocketAddress localAddress; folly::SocketAddress peerAddress; socket_->getLocalAddress(&localAddress); socket_->getPeerAddress(&peerAddress); std::unique_ptr<HTTPCodec> codec; transportInfo_.acceptTime = getCurrentTime(); if (transportInfo_.secure) { AsyncSSLSocket* sslSocket = socket_->getUnderlyingTransport<AsyncSSLSocket>(); if (sslSocket) { transportInfo_.sslNextProtocol = std::make_shared<std::string>(socket_->getApplicationProtocol()); transportInfo_.sslSetupTime = millisecondsSince(connectStart_); transportInfo_.sslCipher = sslSocket->getNegotiatedCipherName() ? std::make_shared<std::string>(sslSocket->getNegotiatedCipherName()) : nullptr; transportInfo_.sslVersion = sslSocket->getSSLVersion(); transportInfo_.sslResume = wangle::SSLUtil::getResumeState(sslSocket); } codec = makeCodec(socket_->getApplicationProtocol(), forceHTTP1xCodecTo1_1_); } else { codec = makeCodec(plaintextProtocol_, forceHTTP1xCodecTo1_1_); } HTTPUpstreamSession* session = new HTTPUpstreamSession( timeout_, std::move(socket_), localAddress, peerAddress, std::move(codec), transportInfo_, nullptr); cb_->connectSuccess(session); }
SSL_SESSION* SSLSessionCacheManager::getSession(SSL* ssl, unsigned char* session_id, int id_len, int* copyflag) { VLOG(7) << "SSL get session callback"; SSL_SESSION* session = nullptr; bool foreign = false; char const* missReason = nullptr; if (id_len < MIN_SESSION_ID_LENGTH) { // We didn't generate this session so it's going to be a miss. // This doesn't get logged or counted in the stats. return nullptr; } string sessionId((char*)session_id, id_len); AsyncSSLSocket* sslSocket = AsyncSSLSocket::getFromSSL(ssl); assert(sslSocket != nullptr); // look it up in the local cache first session = localCache_->lookupSession(sessionId); #ifdef SSL_SESSION_CB_WOULD_BLOCK if (session == nullptr && externalCache_) { // external cache might have the session foreign = true; if (!SSL_want_sess_cache_lookup(ssl)) { missReason = "reason: No async cache support;"; } else { PendingLookupMap::iterator pit = pendingLookups_.find(sessionId); if (pit == pendingLookups_.end()) { auto result = pendingLookups_.emplace(sessionId, PendingLookup()); // initiate fetch VLOG(4) << "Get SSL session [Pending]: Initiate Fetch; fd=" << sslSocket->getFd() << " id=" << SSLUtil::hexlify(sessionId); if (lookupCacheRecord(sessionId, sslSocket)) { // response is pending *copyflag = SSL_SESSION_CB_WOULD_BLOCK; return nullptr; } else { missReason = "reason: failed to send lookup request;"; pendingLookups_.erase(result.first); } } else { // A lookup was already initiated from this thread if (pit->second.request_in_progress) { // Someone else initiated the request, attach VLOG(4) << "Get SSL session [Pending]: Request in progess: attach; " "fd=" << sslSocket->getFd() << " id=" << SSLUtil::hexlify(sessionId); std::unique_ptr<DelayedDestruction::DestructorGuard> dg( new DelayedDestruction::DestructorGuard(sslSocket)); pit->second.waiters.emplace_back(sslSocket, std::move(dg)); *copyflag = SSL_SESSION_CB_WOULD_BLOCK; return nullptr; } // request is complete session = pit->second.session; // nullptr if our friend didn't have it if (session != nullptr) { CRYPTO_add(&session->references, 1, CRYPTO_LOCK_SSL_SESSION); } } } } #endif bool hit = (session != nullptr); if (stats_) { stats_->recordSSLSession(false, hit, foreign); } if (hit) { sslSocket->setSessionIDResumed(true); } VLOG(4) << "Get SSL session [" << ((hit) ? "Hit" : "Miss") << "]: " << ((foreign) ? "external" : "local") << " cache; " << ((missReason != nullptr) ? missReason : "") << "fd=" << sslSocket->getFd() << " id=" << SSLUtil::hexlify(sessionId); // We already bumped the refcount *copyflag = 0; return session; }
void AsyncSSLSocket::clientHelloParsingCallback(int written, int version, int contentType, const void *buf, size_t len, SSL *ssl, void *arg) { AsyncSSLSocket *sock = static_cast<AsyncSSLSocket*>(arg); if (written != 0) { sock->resetClientHelloParsing(ssl); return; } if (contentType != SSL3_RT_HANDSHAKE) { sock->resetClientHelloParsing(ssl); return; } if (len == 0) { return; } auto& clientHelloBuf = sock->clientHelloInfo_->clientHelloBuf_; clientHelloBuf.append(IOBuf::wrapBuffer(buf, len)); try { Cursor cursor(clientHelloBuf.front()); if (cursor.read<uint8_t>() != SSL3_MT_CLIENT_HELLO) { sock->resetClientHelloParsing(ssl); return; } if (cursor.totalLength() < 3) { clientHelloBuf.trimEnd(len); clientHelloBuf.append(IOBuf::copyBuffer(buf, len)); return; } uint32_t messageLength = cursor.read<uint8_t>(); messageLength <<= 8; messageLength |= cursor.read<uint8_t>(); messageLength <<= 8; messageLength |= cursor.read<uint8_t>(); if (cursor.totalLength() < messageLength) { clientHelloBuf.trimEnd(len); clientHelloBuf.append(IOBuf::copyBuffer(buf, len)); return; } sock->clientHelloInfo_->clientHelloMajorVersion_ = cursor.read<uint8_t>(); sock->clientHelloInfo_->clientHelloMinorVersion_ = cursor.read<uint8_t>(); cursor.skip(4); // gmt_unix_time cursor.skip(28); // random_bytes cursor.skip(cursor.read<uint8_t>()); // session_id uint16_t cipherSuitesLength = cursor.readBE<uint16_t>(); for (int i = 0; i < cipherSuitesLength; i += 2) { sock->clientHelloInfo_-> clientHelloCipherSuites_.push_back(cursor.readBE<uint16_t>()); } uint8_t compressionMethodsLength = cursor.read<uint8_t>(); for (int i = 0; i < compressionMethodsLength; ++i) { sock->clientHelloInfo_-> clientHelloCompressionMethods_.push_back(cursor.readBE<uint8_t>()); } if (cursor.totalLength() > 0) { uint16_t extensionsLength = cursor.readBE<uint16_t>(); while (extensionsLength) { sock->clientHelloInfo_-> clientHelloExtensions_.push_back(cursor.readBE<uint16_t>()); extensionsLength -= 2; uint16_t extensionDataLength = cursor.readBE<uint16_t>(); extensionsLength -= 2; cursor.skip(extensionDataLength); extensionsLength -= extensionDataLength; } } } catch (std::out_of_range& e) { // we'll use what we found and cleanup below. VLOG(4) << "AsyncSSLSocket::clientHelloParsingCallback(): " << "buffer finished unexpectedly." << " AsyncSSLSocket socket=" << sock; } sock->resetClientHelloParsing(ssl); }