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