• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 The Android Open Source Project
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 #ifndef CONSCRYPT_APP_DATA_H_
18 #define CONSCRYPT_APP_DATA_H_
19 
20 #include <conscrypt/NetFd.h>
21 #include <conscrypt/compat.h>
22 #include <conscrypt/jniutil.h>
23 #include <conscrypt/netutil.h>
24 #include <conscrypt/trace.h>
25 
26 #include <jni.h>
27 #include <atomic>
28 #include <mutex>  // NOLINT(build/c++11)
29 
30 #ifdef _WIN32
31 // Needed for inet_ntop
32 #define _WIN32_WINNT _WIN32_WINNT_WIN8
33 #include <ws2ipdef.h>
34 #include <ws2tcpip.h>
35 #pragma comment(lib, "ws2_32.lib")
36 
37 #include <winsock2.h>
38 #else  // !_WIN32
39 #include <arpa/inet.h>
40 #include <poll.h>
41 #include <unistd.h>
42 #endif  // !_WIN32
43 
44 namespace conscrypt {
45 
46 /**
47  * Our additional application data needed for getting synchronization right.
48  * This maybe warrants a bit of lengthy prose:
49  *
50  * (1) We use a flag to reflect whether we consider the SSL connection alive.
51  * Any read or write attempt loops will be cancelled once this flag becomes 0.
52  *
53  * (2) We use an int to count the number of threads that are blocked by the
54  * underlying socket. This may be at most two (one reader and one writer), since
55  * the Java layer ensures that no more threads will enter the native code at the
56  * same time.
57  *
58  * (3) The pipe is used primarily as a means of cancelling a blocking select()
59  * when we want to close the connection (aka "emergency button"). It is also
60  * necessary for dealing with a possible race condition situation: There might
61  * be cases where both threads see an SSL_ERROR_WANT_READ or
62  * SSL_ERROR_WANT_WRITE. Both will enter a select() with the proper argument.
63  * If one leaves the select() successfully before the other enters it, the
64  * "success" event is already consumed and the second thread will be blocked,
65  * possibly forever (depending on network conditions).
66  *
67  * The idea for solving the problem looks like this: Whenever a thread is
68  * successful in moving around data on the network, and it knows there is
69  * another thread stuck in a select(), it will write a byte to the pipe, waking
70  * up the other thread. A thread that returned from select(), on the other hand,
71  * knows whether it's been woken up by the pipe. If so, it will consume the
72  * byte, and the original state of affairs has been restored.
73  *
74  * The pipe may seem like a bit of overhead, but it fits in nicely with the
75  * other file descriptors of the select(), so there's only one condition to wait
76  * for.
77  *
78  * (4) Finally, a mutex is needed to make sure that at most one thread is in
79  * either SSL_read() or SSL_write() at any given time. This is an OpenSSL
80  * requirement. We use the same mutex to guard the field for counting the
81  * waiting threads.
82  *
83  * During handshaking, additional fields are used to up-call into
84  * Java to perform certificate verification and handshake
85  * completion. These are also used in any renegotiation.
86  *
87  * (5) the JNIEnv so we can invoke the Java callback
88  *
89  * (6) a NativeCrypto.SSLHandshakeCallbacks instance for callbacks from native to Java
90  *
91  * (7) a java.io.FileDescriptor wrapper to check for socket close
92  *
93  * We store the ALPN protocols list so we can either send it (from the server) or
94  * select a protocol (on the client). We eagerly acquire a pointer to the array
95  * data so the callback doesn't need to acquire resources that it cannot
96  * release.
97  *
98  * Because renegotiation can be requested by the peer at any time,
99  * care should be taken to maintain an appropriate JNIEnv on any
100  * downcall to openssl since it could result in an upcall to Java. The
101  * current code does try to cover these cases by conditionally setting
102  * the JNIEnv on calls that can read and write to the SSL such as
103  * SSL_do_handshake, SSL_read, SSL_write, and SSL_shutdown.
104  */
105 class AppData {
106  public:
107     std::atomic<bool> aliveAndKicking;
108     int waitingThreads;
109 #ifdef _WIN32
110     HANDLE interruptEvent;
111 #else
112     int fdsEmergency[2];
113 #endif
114     std::mutex mutex;
115     JNIEnv* env;
116     jobject sslHandshakeCallbacks;
117     char* applicationProtocolsData;
118     size_t applicationProtocolsLength;
119     jobject applicationProtocolSelector;
120 
121     /**
122      * Creates the application data context for the SSL*.
123      */
create()124     static AppData* create() {
125         std::unique_ptr<AppData> appData(new AppData());
126 #ifdef _WIN32
127         HANDLE interruptEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
128         if (interruptEvent == nullptr) {
129             JNI_TRACE("AppData::create WSACreateEvent failed: %d", WSAGetLastError());
130             return nullptr;
131         }
132         appData.get()->interruptEvent = interruptEvent;
133 #else
134         if (pipe(appData.get()->fdsEmergency) == -1) {
135             CONSCRYPT_LOG_ERROR("AppData::create pipe(2) failed: %s", strerror(errno));
136             return nullptr;
137         }
138         if (!netutil::setBlocking(appData.get()->fdsEmergency[0], false)) {
139             CONSCRYPT_LOG_ERROR("AppData::create fcntl(2) failed: %s", strerror(errno));
140             return nullptr;
141         }
142 #endif
143         return appData.release();
144     }
145 
~AppData()146     ~AppData() {
147         aliveAndKicking = false;
148 #ifdef _WIN32
149         if (interruptEvent != nullptr) {
150             CloseHandle(interruptEvent);
151         }
152 #else
153         if (fdsEmergency[0] != -1) {
154             close(fdsEmergency[0]);
155         }
156         if (fdsEmergency[1] != -1) {
157             close(fdsEmergency[1]);
158         }
159 #endif
160         clearApplicationProtocols();
161         clearApplicationProtocolSelector(env);
162         clearCallbackState();
163     }
164 
165     /**
166      * Only called in server mode. Sets the protocols for ALPN negotiation.
167      *
168      * @param env The JNIEnv
169      * @param alpnProtocols ALPN protocols so that they may be advertised (by the
170      *                     server) or selected (by the client). Passing
171      *                     non-null enables ALPN. This array is copied so that no
172      *                     global reference to the Java byte array is maintained.
173      */
setApplicationProtocols(JNIEnv * e,jbyteArray applicationProtocolsJava)174     bool setApplicationProtocols(JNIEnv* e, jbyteArray applicationProtocolsJava) {
175         clearApplicationProtocols();
176         if (applicationProtocolsJava != nullptr) {
177             jbyte* applicationProtocols =
178                 e->GetByteArrayElements(applicationProtocolsJava, nullptr);
179             if (applicationProtocols == nullptr) {
180                 clearCallbackState();
181                 JNI_TRACE("appData=%p setApplicationCallbackState => applicationProtocols == null",
182                           this);
183                 return false;
184             }
185             applicationProtocolsLength =
186                 static_cast<size_t>(e->GetArrayLength(applicationProtocolsJava));
187             applicationProtocolsData = new char[applicationProtocolsLength];
188             memcpy(applicationProtocolsData, applicationProtocols, applicationProtocolsLength);
189             e->ReleaseByteArrayElements(applicationProtocolsJava, applicationProtocols, JNI_ABORT);
190         }
191         return true;
192     }
193 
194     /**
195      * Only called in server mode. Sets the application-provided ALPN protocol selector.
196      * This overrides the list of ALPN protocols, if set.
197      */
setApplicationProtocolSelector(JNIEnv * e,jobject selector)198     void setApplicationProtocolSelector(JNIEnv* e, jobject selector) {
199         clearApplicationProtocolSelector(e);
200         if (selector != nullptr) {
201             // Need to a global reference since we keep this around beyond a single JNI
202             // invocation.
203             applicationProtocolSelector = e->NewGlobalRef(selector);
204         }
205     }
206 
207     /**
208      * Used to set the SSL-to-Java callback state before each SSL_*
209      * call that may result in a callback. It should be cleared after
210      * the operation returns with clearCallbackState.
211      *
212      * @param env The JNIEnv
213      * @param shc The SSLHandshakeCallbacks
214      * @param fd The FileDescriptor
215      */
setCallbackState(JNIEnv * e,jobject shc,jobject fd)216     bool setCallbackState(JNIEnv* e, jobject shc, jobject fd) {
217         std::unique_ptr<NetFd> netFd;
218         if (fd != nullptr) {
219             netFd.reset(new NetFd(e, fd));
220             if (netFd->isClosed()) {
221                 JNI_TRACE("appData=%p setCallbackState => netFd->isClosed() == true", this);
222                 return false;
223             }
224         }
225         env = e;
226         sslHandshakeCallbacks = shc;
227         return true;
228     }
229 
clearCallbackState()230     void clearCallbackState() {
231         sslHandshakeCallbacks = nullptr;
232         env = nullptr;
233     }
234 
235  private:
AppData()236     AppData()
237         : aliveAndKicking(true),
238           waitingThreads(0),
239           env(nullptr),
240           sslHandshakeCallbacks(nullptr),
241           applicationProtocolsData(nullptr),
242           applicationProtocolsLength(static_cast<size_t>(-1)),
243           applicationProtocolSelector(nullptr) {
244 #ifdef _WIN32
245         interruptEvent = nullptr;
246 #else
247         fdsEmergency[0] = -1;
248         fdsEmergency[1] = -1;
249 #endif
250     }
251 
clearApplicationProtocols()252     void clearApplicationProtocols() {
253         if (applicationProtocolsData != nullptr) {
254             delete applicationProtocolsData;
255             applicationProtocolsData = nullptr;
256             applicationProtocolsLength = static_cast<size_t>(-1);
257         }
258     }
259 
clearApplicationProtocolSelector(JNIEnv * e)260     void clearApplicationProtocolSelector(JNIEnv* e) {
261         if (applicationProtocolSelector != nullptr) {
262             if (e == nullptr) {
263                 e = conscrypt::jniutil::getJNIEnv();
264             }
265             e->DeleteGlobalRef(applicationProtocolSelector);
266             applicationProtocolSelector = nullptr;
267         }
268     }
269 };
270 
271 }  // namespace conscrypt
272 
273 #endif  // CONSCRYPT_APP_DATA_H_
274