close
Skip to content

Commit c47d0c7

Browse files
frqiufacebook-github-bot
authored andcommitted
Parse and capture client alpns from client hello info
Summary: Add code to capture client alpns from client hello packet and store them in ssl socket object for later logging. Reviewed By: AjanthanAsogamoorthy Differential Revision: D31176714 fbshipit-source-id: 888fd9949ede5209234bb3ab1959a6f9c14043b2
1 parent dfe1356 commit c47d0c7

8 files changed

Lines changed: 77 additions & 0 deletions

File tree

‎folly/io/async/AsyncSSLSocket.cpp‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,21 @@ void AsyncSSLSocket::resetClientHelloParsing(SSL* ssl) {
19271927
clientHelloInfo_->clientHelloBuf_.clear();
19281928
}
19291929

1930+
void AsyncSSLSocket::parseClientAlpns(
1931+
AsyncSSLSocket* sock,
1932+
folly::io::Cursor& cursor,
1933+
uint16_t& extensionDataLength) {
1934+
cursor.skip(2);
1935+
extensionDataLength -= 2;
1936+
while (extensionDataLength) {
1937+
auto protoLength = cursor.readBE<uint8_t>();
1938+
extensionDataLength--;
1939+
auto proto = cursor.readFixedString(protoLength);
1940+
sock->clientHelloInfo_->clientAlpns_.push_back(proto);
1941+
extensionDataLength -= protoLength;
1942+
}
1943+
}
1944+
19301945
void AsyncSSLSocket::clientHelloParsingCallback(
19311946
int written,
19321947
int /* version */,
@@ -2051,6 +2066,10 @@ void AsyncSSLSocket::clientHelloParsingCallback(
20512066
extensionDataLength -=
20522067
sizeof(typ) + sizeof(nameLength) + nameLength;
20532068
}
2069+
} else if (
2070+
extensionType ==
2071+
ssl::TLSExtension::APPLICATION_LAYER_PROTOCOL_NEGOTIATION) {
2072+
parseClientAlpns(sock, cursor, extensionDataLength);
20542073
} else {
20552074
cursor.skip(extensionDataLength);
20562075
}
@@ -2189,4 +2208,13 @@ void AsyncSSLSocket::getSSLServerCiphers(std::string& serverCiphers) const {
21892208
}
21902209
}
21912210

2211+
const std::vector<std::string>& AsyncSSLSocket::getClientAlpns() const {
2212+
if (!parseClientHello_) {
2213+
static std::vector<std::string> emptyAlpns{};
2214+
return emptyAlpns;
2215+
} else {
2216+
return clientHelloInfo_->clientAlpns_;
2217+
}
2218+
}
2219+
21922220
} // namespace folly

‎folly/io/async/AsyncSSLSocket.h‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -768,6 +768,13 @@ class AsyncSSLSocket : public AsyncSocket {
768768
*/
769769
void getSSLServerCiphers(std::string& serverCiphers) const;
770770

771+
/**
772+
* Get the list of next protocols sent from the client. The protocols are
773+
* directly as the client passed them and may be arbitrary byte sequences
774+
* of arbitrary length.
775+
*/
776+
const std::vector<std::string>& getClientAlpns() const;
777+
771778
/**
772779
* Method to check if peer verfication is set.
773780
*
@@ -780,6 +787,10 @@ class AsyncSSLSocket : public AsyncSocket {
780787
static int bioWrite(BIO* b, const char* in, int inl);
781788
static int bioRead(BIO* b, char* out, int outl);
782789
void resetClientHelloParsing(SSL* ssl);
790+
static void parseClientAlpns(
791+
AsyncSSLSocket* sock,
792+
folly::io::Cursor& cursor,
793+
uint16_t& extensionDataLength);
783794
static void clientHelloParsingCallback(
784795
int written,
785796
int version,

‎folly/io/async/SSLContext.cpp‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -600,6 +600,22 @@ int SSLContext::alpnSelectCallback(
600600
return SSL_TLSEXT_ERR_OK;
601601
}
602602

603+
std::string SSLContext::getAdvertisedNextProtocols() {
604+
if (advertisedNextProtocols_.empty()) {
605+
return "";
606+
}
607+
std::string alpns(
608+
(const char*)advertisedNextProtocols_[0].protocols + 1,
609+
advertisedNextProtocols_[0].length - 1);
610+
auto len = advertisedNextProtocols_[0].protocols[0];
611+
for (size_t i = len; i < alpns.length();) {
612+
len = alpns[i];
613+
alpns[i] = ',';
614+
i += len + 1;
615+
}
616+
return alpns;
617+
}
618+
603619
bool SSLContext::setAdvertisedNextProtocols(
604620
const std::list<std::string>& protocols) {
605621
return setRandomizedAdvertisedNextProtocols({{1, protocols}});

‎folly/io/async/SSLContext.h‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,6 +487,8 @@ class SSLContext {
487487
void setOptions(long options);
488488

489489
#if FOLLY_OPENSSL_HAS_ALPN
490+
std::string getAdvertisedNextProtocols();
491+
490492
/**
491493
* Set the list of protocols that this SSL context supports. In client
492494
* mode, this is the list of protocols that will be advertised for Application

‎folly/io/async/ssl/TLSDefinitions.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ struct ClientHelloInfo {
9696
// long as each ServerName has a distinct type). In practice, the only one
9797
// we really care about is HOST_NAME.
9898
std::string clientHelloSNIHostname_;
99+
std::vector<std::string> clientAlpns_;
99100
};
100101

101102
} // namespace ssl

‎folly/io/async/test/AsyncSSLSocketTest.cpp‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,7 @@ TEST_F(NextProtocolTest, AlpnNotAllowMismatchNoClientProtocol) {
597597

598598
expectHandshakeSuccess();
599599
expectNoProtocol();
600+
EXPECT_EQ(server->getClientAlpns(), std::vector<std::string>({}));
600601
}
601602

602603
TEST_F(NextProtocolTest, AlpnNotAllowMismatchWithOverlap) {
@@ -607,6 +608,8 @@ TEST_F(NextProtocolTest, AlpnNotAllowMismatchWithOverlap) {
607608
connect();
608609

609610
expectProtocol("baz");
611+
EXPECT_EQ(
612+
server->getClientAlpns(), std::vector<std::string>({"blub", "baz"}));
610613
}
611614

612615
TEST_F(NextProtocolTest, AlpnNotAllowMismatchWithoutOverlap) {
@@ -617,6 +620,7 @@ TEST_F(NextProtocolTest, AlpnNotAllowMismatchWithoutOverlap) {
617620
connect();
618621

619622
expectHandshakeError();
623+
EXPECT_EQ(server->getClientAlpns(), std::vector<std::string>({"blub"}));
620624
}
621625

622626
#endif

‎folly/io/async/test/AsyncSSLSocketTest.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -875,11 +875,15 @@ class AlpnServer : private AsyncSSLSocket::HandshakeCB,
875875
explicit AlpnServer(AsyncSSLSocket::UniquePtr socket)
876876
: nextProto(nullptr), nextProtoLength(0), socket_(std::move(socket)) {
877877
socket_->sslAccept(this);
878+
socket_->enableClientHelloParsing();
878879
}
879880

880881
const unsigned char* nextProto;
881882
unsigned nextProtoLength;
882883
folly::Optional<AsyncSocketException> except;
884+
const std::vector<std::string>& getClientAlpns() const {
885+
return socket_->getClientAlpns();
886+
}
883887

884888
private:
885889
void handshakeSuc(AsyncSSLSocket*) noexcept override {

‎folly/io/async/test/SSLContextTest.cpp‎

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,15 @@ TEST_F(SSLContextTest, TestSetInvalidCiphersuite) {
235235
TEST_F(SSLContextTest, TestTLS13MinVersionThrow) {
236236
EXPECT_THROW(SSLContext{SSLContext::SSLVersion::TLSv1_3}, std::runtime_error);
237237
}
238+
239+
TEST_F(SSLContextTest, AdvertisedNextProtocols) {
240+
EXPECT_EQ(ctx.getAdvertisedNextProtocols(), "");
241+
242+
ctx.setAdvertisedNextProtocols({"blub"});
243+
EXPECT_EQ(ctx.getAdvertisedNextProtocols(), "blub");
244+
245+
ctx.setAdvertisedNextProtocols({"foo", "bar", "baz"});
246+
EXPECT_EQ(ctx.getAdvertisedNextProtocols(), "foo,bar,baz");
247+
}
248+
238249
} // namespace folly

0 commit comments

Comments
 (0)