2017
[folly.git] / folly / io / async / test / SSLSessionTest.cpp
1 /*
2  * Copyright 2017 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <folly/io/async/test/AsyncSSLSocketTest.h>
18 #include <folly/portability/GTest.h>
19 #include <folly/portability/Sockets.h>
20 #include <folly/ssl/SSLSession.h>
21
22 using namespace std;
23 using namespace testing;
24 using folly::ssl::SSLSession;
25
26 namespace folly {
27
28 const char* testCert = "folly/io/async/test/certs/tests-cert.pem";
29 const char* testKey = "folly/io/async/test/certs/tests-key.pem";
30 const char* testCA = "folly/io/async/test/certs/ca-cert.pem";
31
32 void getfds(int fds[2]) {
33   if (socketpair(PF_LOCAL, SOCK_STREAM, 0, fds) != 0) {
34     LOG(ERROR) << "failed to create socketpair: " << strerror(errno);
35   }
36   for (int idx = 0; idx < 2; ++idx) {
37     int flags = fcntl(fds[idx], F_GETFL, 0);
38     if (flags == -1) {
39       LOG(ERROR) << "failed to get flags for socket " << idx << ": "
40                  << strerror(errno);
41     }
42     if (fcntl(fds[idx], F_SETFL, flags | O_NONBLOCK) != 0) {
43       LOG(ERROR) << "failed to put socket " << idx
44                  << " in non-blocking mode: " << strerror(errno);
45     }
46   }
47 }
48
49 void getctx(
50     std::shared_ptr<folly::SSLContext> clientCtx,
51     std::shared_ptr<folly::SSLContext> serverCtx) {
52   clientCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
53
54   serverCtx->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
55   serverCtx->loadCertificate(testCert);
56   serverCtx->loadPrivateKey(testKey);
57 }
58
59 class SSLSessionTest : public testing::Test {
60  public:
61   void SetUp() override {
62     clientCtx.reset(new folly::SSLContext());
63     dfServerCtx.reset(new folly::SSLContext());
64     hskServerCtx.reset(new folly::SSLContext());
65     serverName = "xyz.newdev.facebook.com";
66     getctx(clientCtx, dfServerCtx);
67   }
68
69   void TearDown() override {}
70
71   folly::EventBase eventBase;
72   std::shared_ptr<SSLContext> clientCtx;
73   std::shared_ptr<SSLContext> dfServerCtx;
74   // Use the same SSLContext to continue the handshake after
75   // tlsext_hostname match.
76   std::shared_ptr<SSLContext> hskServerCtx;
77   std::string serverName;
78 };
79
80 /**
81  * 1. Client sends TLSEXT_HOSTNAME in client hello.
82  * 2. Server found a match SSL_CTX and use this SSL_CTX to
83  *    continue the SSL handshake.
84  * 3. Server sends back TLSEXT_HOSTNAME in server hello.
85  */
86 TEST_F(SSLSessionTest, BasicTest) {
87   std::unique_ptr<SSLSession> sess;
88
89   {
90     int fds[2];
91     getfds(fds);
92     AsyncSSLSocket::UniquePtr clientSock(
93         new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
94     auto clientPtr = clientSock.get();
95     AsyncSSLSocket::UniquePtr serverSock(
96         new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
97     SSLHandshakeClient client(std::move(clientSock), false, false);
98     SSLHandshakeServerParseClientHello server(
99         std::move(serverSock), false, false);
100
101     eventBase.loop();
102     ASSERT_TRUE(client.handshakeSuccess_);
103
104     sess.reset(new SSLSession(clientPtr->getSSLSession()));
105     ASSERT_NE(sess.get(), nullptr);
106   }
107
108   {
109     int fds[2];
110     getfds(fds);
111     AsyncSSLSocket::UniquePtr clientSock(
112         new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
113     auto clientPtr = clientSock.get();
114     clientSock->setSSLSession(sess->getRawSSLSessionDangerous(), true);
115     AsyncSSLSocket::UniquePtr serverSock(
116         new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
117     SSLHandshakeClient client(std::move(clientSock), false, false);
118     SSLHandshakeServerParseClientHello server(
119         std::move(serverSock), false, false);
120
121     eventBase.loop();
122     ASSERT_TRUE(client.handshakeSuccess_);
123     ASSERT_TRUE(clientPtr->getSSLSessionReused());
124   }
125 }
126 TEST_F(SSLSessionTest, SerializeDeserializeTest) {
127   std::string sessiondata;
128
129   {
130     int fds[2];
131     getfds(fds);
132     AsyncSSLSocket::UniquePtr clientSock(
133         new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
134     auto clientPtr = clientSock.get();
135     AsyncSSLSocket::UniquePtr serverSock(
136         new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
137     SSLHandshakeClient client(std::move(clientSock), false, false);
138     SSLHandshakeServerParseClientHello server(
139         std::move(serverSock), false, false);
140
141     eventBase.loop();
142     ASSERT_TRUE(client.handshakeSuccess_);
143
144     std::unique_ptr<SSLSession> sess =
145         folly::make_unique<SSLSession>(clientPtr->getSSLSession());
146     sessiondata = sess->serialize();
147     ASSERT_TRUE(!sessiondata.empty());
148   }
149
150   {
151     int fds[2];
152     getfds(fds);
153     AsyncSSLSocket::UniquePtr clientSock(
154         new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
155     auto clientPtr = clientSock.get();
156     std::unique_ptr<SSLSession> sess =
157         folly::make_unique<SSLSession>(sessiondata);
158     ASSERT_NE(sess.get(), nullptr);
159     clientSock->setSSLSession(sess->getRawSSLSessionDangerous(), true);
160     AsyncSSLSocket::UniquePtr serverSock(
161         new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
162     SSLHandshakeClient client(std::move(clientSock), false, false);
163     SSLHandshakeServerParseClientHello server(
164         std::move(serverSock), false, false);
165
166     eventBase.loop();
167     ASSERT_TRUE(client.handshakeSuccess_);
168     ASSERT_TRUE(clientPtr->getSSLSessionReused());
169   }
170 }
171
172 TEST_F(SSLSessionTest, GetSessionID) {
173   int fds[2];
174   getfds(fds);
175   AsyncSSLSocket::UniquePtr clientSock(
176       new AsyncSSLSocket(clientCtx, &eventBase, fds[0], serverName));
177   auto clientPtr = clientSock.get();
178   AsyncSSLSocket::UniquePtr serverSock(
179       new AsyncSSLSocket(dfServerCtx, &eventBase, fds[1], true));
180   SSLHandshakeClient client(std::move(clientSock), false, false);
181   SSLHandshakeServerParseClientHello server(
182       std::move(serverSock), false, false);
183
184   eventBase.loop();
185   ASSERT_TRUE(client.handshakeSuccess_);
186
187   std::unique_ptr<SSLSession> sess =
188       folly::make_unique<SSLSession>(clientPtr->getSSLSession());
189   ASSERT_NE(sess, nullptr);
190   auto sessID = sess->getSessionID();
191   ASSERT_GE(sessID.length(), 0);
192 }
193 }