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