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