diff options
Diffstat (limited to 'luni/src/main/native/NativeCrypto.cpp')
-rw-r--r-- | luni/src/main/native/NativeCrypto.cpp | 295 |
1 files changed, 103 insertions, 192 deletions
diff --git a/luni/src/main/native/NativeCrypto.cpp b/luni/src/main/native/NativeCrypto.cpp index f94b7e7..0f9ae1d 100644 --- a/luni/src/main/native/NativeCrypto.cpp +++ b/luni/src/main/native/NativeCrypto.cpp @@ -20,6 +20,7 @@ #define LOG_TAG "NativeCrypto" +#include <algorithm> #include <fcntl.h> #include <sys/socket.h> #include <unistd.h> @@ -37,7 +38,6 @@ #include "JNIHelp.h" #include "JniConstants.h" #include "JniException.h" -#include "LocalArray.h" #include "NetFd.h" #include "NetworkUtilities.h" #include "ScopedLocalRef.h" @@ -46,6 +46,8 @@ #include "UniquePtr.h" #undef WITH_JNI_TRACE +#undef WITH_JNI_TRACE_DATA + #ifdef WITH_JNI_TRACE #define JNI_TRACE(...) \ ((void)LOG(LOG_INFO, LOG_TAG "-jni", __VA_ARGS__)); \ @@ -57,6 +59,8 @@ #else #define JNI_TRACE(...) ((void)0) #endif +// don't overwhelm logcat +#define WITH_JNI_TRACE_DATA_CHUNK_SIZE 512 struct BIO_Delete { void operator()(BIO* p) const { @@ -86,6 +90,13 @@ struct DSA_Delete { }; typedef UniquePtr<DSA, DSA_Delete> Unique_DSA; +struct EC_KEY_Delete { + void operator()(EC_KEY* p) const { + EC_KEY_free(p); + } +}; +typedef UniquePtr<EC_KEY, EC_KEY_Delete> Unique_EC_KEY; + struct EVP_PKEY_Delete { void operator()(EVP_PKEY* p) const { EVP_PKEY_free(p); @@ -619,19 +630,19 @@ static jint NativeCrypto_EVP_MD_CTX_copy(JNIEnv* env, jclass, EVP_MD_CTX* ctx) { if (ctx == NULL) { jniThrowNullPointerException(env, NULL); - return NULL; + return 0; } EVP_MD_CTX* copy = EVP_MD_CTX_create(); if (copy == NULL) { jniThrowOutOfMemoryError(env, "Unable to allocate copy of EVP_MD_CTX"); - return NULL; + return 0; } EVP_MD_CTX_init(copy); int result = EVP_MD_CTX_copy_ex(copy, ctx); if (result == 0) { EVP_MD_CTX_destroy(copy); jniThrowRuntimeException(env, "Unable to copy EVP_MD_CTX"); - return NULL; + return 0; } JNI_TRACE("NativeCrypto_EVP_MD_CTX_copy(%p) => %p", ctx, copy); return (jint) copy; @@ -987,72 +998,6 @@ static int NativeCrypto_RAND_load_file(JNIEnv* env, jclass, jstring filename, jl return result; } -/** - * Convert ssl version constant to string. Based on SSL_get_version - */ -// TODO move to jsse.patch -static const char* get_ssl_version(int ssl_version) { - switch (ssl_version) { - // newest to oldest - case TLS1_VERSION: { - return SSL_TXT_TLSV1; - } - case SSL3_VERSION: { - return SSL_TXT_SSLV3; - } - case SSL2_VERSION: { - return SSL_TXT_SSLV2; - } - default: { - return "unknown"; - } - } -} - -#ifdef WITH_JNI_TRACE -/** - * Convert content type constant to string. - */ -// TODO move to jsse.patch -static const char* get_content_type(int content_type) { - switch (content_type) { - case SSL3_RT_CHANGE_CIPHER_SPEC: { - return "SSL3_RT_CHANGE_CIPHER_SPEC"; - } - case SSL3_RT_ALERT: { - return "SSL3_RT_ALERT"; - } - case SSL3_RT_HANDSHAKE: { - return "SSL3_RT_HANDSHAKE"; - } - case SSL3_RT_APPLICATION_DATA: { - return "SSL3_RT_APPLICATION_DATA"; - } - default: { - LOGD("Unknown TLS/SSL content type %d", content_type); - return "<unknown>"; - } - } -} -#endif - -#ifdef WITH_JNI_TRACE -/** - * Simple logging call back to show hand shake messages - */ -static void ssl_msg_callback_LOG(int write_p, int ssl_version, int content_type, - const void* buf, size_t len, SSL* ssl, void* arg) { - JNI_TRACE("ssl=%p SSL msg %s %s %s %p %d %p", - ssl, - (write_p) ? "send" : "recv", - get_ssl_version(ssl_version), - get_content_type(content_type), - buf, - len, - arg); -} -#endif - #ifdef WITH_JNI_TRACE /** * Based on example logging call back from SSL_CTX_set_info_callback man page @@ -1259,11 +1204,14 @@ static jobjectArray getPrincipalBytes(JNIEnv* env, const STACK_OF(X509_NAME)* na * the JNIEnv on calls that can read and write to the SSL such as * SSL_do_handshake, SSL_read, SSL_write, and SSL_shutdown. * - * Finally, we have one other piece of state setup by OpenSSL callbacks: + * Finally, we have two emphemeral keys setup by OpenSSL callbacks: * * (8) a set of ephemeral RSA keys that is lazily generated if a peer * wants to use an exportable RSA cipher suite. * + * (9) a set of ephemeral EC keys that is lazily generated if a peer + * wants to use an TLS_ECDHE_* cipher suite. + * */ class AppData { public: @@ -1275,6 +1223,7 @@ class AppData { jobject sslHandshakeCallbacks; jobject fileDescriptor; Unique_RSA ephemeralRsa; + Unique_EC_KEY ephemeralEc; /** * Creates the application data context for the SSL*. @@ -1285,6 +1234,9 @@ class AppData { if (pipe(appData.get()->fdsEmergency) == -1) { return NULL; } + if (!setBlocking(appData.get()->fdsEmergency[0], false)) { + return NULL; + } if (MUTEX_SETUP(appData.get()->mutex) == -1) { return NULL; } @@ -1308,7 +1260,8 @@ class AppData { waitingThreads(0), env(NULL), sslHandshakeCallbacks(NULL), - ephemeralRsa(NULL) { + ephemeralRsa(NULL), + ephemeralEc(NULL) { fdsEmergency[0] = -1; fdsEmergency[1] = -1; } @@ -1345,7 +1298,7 @@ class AppData { /** * Dark magic helper function that checks, for a given SSL session, whether it * can SSL_read() or SSL_write() without blocking. Takes into account any - * concurrent attempts to close the SSL session from the Java side. This is + * concurrent attempts to close the SSLSocket from the Java side. This is * needed to get rid of the hangs that occur when thread #1 closes the SSLSocket * while thread #2 is sitting in a blocking read or write. The type argument * specifies whether we are waiting for readability or writability. It expects @@ -1393,7 +1346,7 @@ static int sslSelect(JNIEnv* env, int type, jobject fdObject, AppData* appData, FD_SET(appData->fdsEmergency[0], &rfds); - int max = intFd > appData->fdsEmergency[0] ? intFd : appData->fdsEmergency[0]; + int maxFd = (intFd > appData->fdsEmergency[0]) ? intFd : appData->fdsEmergency[0]; // Build a struct for the timeout data if we actually want a timeout. timeval tv; @@ -1406,32 +1359,34 @@ static int sslSelect(JNIEnv* env, int type, jobject fdObject, AppData* appData, ptv = NULL; } - { - AsynchronousSocketCloseMonitor monitor(intFd); - result = select(max + 1, &rfds, &wfds, NULL, ptv); - JNI_TRACE("sslSelect %s fd=%d appData=%p timeout=%d => %d", - (type == SSL_ERROR_WANT_READ) ? "READ" : "WRITE", - fd.get(), appData, timeout, result); - if (result == -1) { - if (fd.isClosed()) { - result = THROWN_SOCKETEXCEPTION; - break; - } - if (errno != EINTR) { - break; - } + AsynchronousSocketCloseMonitor monitor(intFd); + result = select(maxFd + 1, &rfds, &wfds, NULL, ptv); + JNI_TRACE("sslSelect %s fd=%d appData=%p timeout=%d => %d", + (type == SSL_ERROR_WANT_READ) ? "READ" : "WRITE", + fd.get(), appData, timeout, result); + if (result == -1) { + if (fd.isClosed()) { + result = THROWN_SOCKETEXCEPTION; + break; + } + if (errno != EINTR) { + break; } } } while (result == -1); - // Lock if (MUTEX_LOCK(appData->mutex) == -1) { return -1; } if (result > 0) { - // If we have been woken up by the emergency pipe, there must be a token in - // it. Thus we can safely read it (even in a blocking way). + // We have been woken up by a token in the emergency pipe. We + // can't be sure the token is still in the pipe at this point + // because it could have already been read by the thread that + // originally wrote it if it entered sslSelect and acquired + // the mutex before we did. Thus we cannot safely read from + // the pipe in a blocking way (so we make the pipe + // non-blocking at creation). if (FD_ISSET(appData->fdsEmergency[0], &rfds)) { char token; do { @@ -1444,7 +1399,6 @@ static int sslSelect(JNIEnv* env, int type, jobject fdObject, AppData* appData, // underlying network. appData->waitingThreads--; - // Unlock MUTEX_UNLOCK(appData->mutex); return result; @@ -1470,76 +1424,6 @@ static void sslNotify(AppData* appData) { errno = errnoBackup; } -// From private header file external/openssl/ssl_locl.h -// TODO move dependent code to jsse.patch to avoid dependency -#define SSL_aRSA 0x00000001L -#define SSL_aDSS 0x00000002L -#define SSL_aNULL 0x00000004L -#define SSL_aDH 0x00000008L -#define SSL_aECDH 0x00000010L -#define SSL_aKRB5 0x00000020L -#define SSL_aECDSA 0x00000040L -#define SSL_aPSK 0x00000080L - -/** - * Converts an SSL_CIPHER's algorithms field to a TrustManager auth argument - */ -// TODO move to jsse.patch -static const char* SSL_CIPHER_authentication_method(const SSL_CIPHER* cipher) -{ - unsigned long alg_auth = cipher->algorithm_auth; - - const char* au; - switch (alg_auth) { - case SSL_aRSA: - au="RSA"; - break; - case SSL_aDSS: - au="DSS"; - break; - case SSL_aDH: - au="DH"; - break; - case SSL_aKRB5: - au="KRB5"; - break; - case SSL_aECDH: - au = "ECDH"; - break; - case SSL_aNULL: - au="None"; - break; - case SSL_aECDSA: - au="ECDSA"; - break; - case SSL_aPSK: - au="PSK"; - break; - default: - au="unknown"; - break; - } - return au; -} - -/** - * Converts an SSL_CIPHER's algorithms field to a TrustManager auth argument - */ -// TODO move to jsse.patch -static const char* SSL_authentication_method(SSL* ssl) -{ - switch (ssl->version) { - case SSL2_VERSION: - return "RSA"; - case SSL3_VERSION: - case TLS1_VERSION: - case DTLS1_VERSION: - return SSL_CIPHER_authentication_method(ssl->s3->tmp.new_cipher); - default: - return "unknown"; - } -} - static AppData* toAppData(const SSL* ssl) { return reinterpret_cast<AppData*>(SSL_get_app_data(ssl)); } @@ -1782,6 +1666,31 @@ static DH* tmp_dh_callback(SSL* ssl __attribute__ ((unused)), return tmp_dh; } +static EC_KEY* ecGenerateKey(int keylength __attribute__ ((unused))) { + // TODO selected curve based on keylength + Unique_EC_KEY ec(EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)); + if (ec.get() == NULL) { + return NULL; + } + return ec.release(); +} + +/** + * Call back to ask for an ephemeral EC key for TLS_ECDHE_* cipher suites + */ +static EC_KEY* tmp_ecdh_callback(SSL* ssl __attribute__ ((unused)), + int is_export __attribute__ ((unused)), + int keylength) { + JNI_TRACE("ssl=%p tmp_ecdh_callback is_export=%d keylength=%d", ssl, is_export, keylength); + AppData* appData = toAppData(ssl); + if (appData->ephemeralEc.get() == NULL) { + JNI_TRACE("ssl=%p tmp_ecdh_callback generating ephemeral EC key", ssl); + appData->ephemeralEc.reset(ecGenerateKey(keylength)); + } + JNI_TRACE("ssl=%p tmp_ecdh_callback => %p", ssl, appData->ephemeralEc.get()); + return appData->ephemeralEc.get(); +} + /* * public static native int SSL_CTX_new(); */ @@ -1789,7 +1698,7 @@ static int NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass) { Unique_SSL_CTX sslCtx(SSL_CTX_new(SSLv23_method())); if (sslCtx.get() == NULL) { jniThrowRuntimeException(env, "SSL_CTX_new"); - return NULL; + return 0; } SSL_CTX_set_options(sslCtx.get(), SSL_OP_ALL @@ -1800,7 +1709,9 @@ static int NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass) { // We also disable compression for better compatibility b/2710492 b/2710497 | SSL_OP_NO_COMPRESSION // Because dhGenerateParameters uses DSA_generate_parameters_ex - | SSL_OP_SINGLE_DH_USE); + | SSL_OP_SINGLE_DH_USE + // Because ecGenerateParameters uses a fixed named curve + | SSL_OP_SINGLE_ECDH_USE); int mode = SSL_CTX_get_mode(sslCtx.get()); /* @@ -1828,10 +1739,8 @@ static int NativeCrypto_SSL_CTX_new(JNIEnv* env, jclass) { SSL_CTX_set_client_cert_cb(sslCtx.get(), client_cert_cb); SSL_CTX_set_tmp_rsa_callback(sslCtx.get(), tmp_rsa_callback); SSL_CTX_set_tmp_dh_callback(sslCtx.get(), tmp_dh_callback); + SSL_CTX_set_tmp_ecdh_callback(sslCtx.get(), tmp_ecdh_callback); -#ifdef WITH_JNI_TRACE - SSL_CTX_set_msg_callback(sslCtx.get(), ssl_msg_callback_LOG); /* enable for message debug */ -#endif JNI_TRACE("NativeCrypto_SSL_CTX_new => %p", sslCtx.get()); return (jint) sslCtx.release(); } @@ -1858,14 +1767,14 @@ static jint NativeCrypto_SSL_new(JNIEnv* env, jclass, jint ssl_ctx_address) SSL_CTX* ssl_ctx = to_SSL_CTX(env, ssl_ctx_address, true); JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new", ssl_ctx); if (ssl_ctx == NULL) { - return NULL; + return 0; } Unique_SSL ssl(SSL_new(ssl_ctx)); if (ssl.get() == NULL) { throwSSLExceptionWithSslErrors(env, NULL, SSL_ERROR_NONE, "Unable to create SSL structure"); JNI_TRACE("ssl_ctx=%p NativeCrypto_SSL_new => NULL", ssl_ctx); - return NULL; + return 0; } /* Java code in class OpenSSLSocketImpl does the verification. Meaning of @@ -1917,6 +1826,8 @@ static void NativeCrypto_SSL_use_PrivateKey(JNIEnv* env, jclass, return; } + JNI_TRACE("ssl=%p NativeCrypto_SSL_use_PrivateKey EVP_PKEY_type=%d", + ssl, EVP_PKEY_type(privatekeyevp.get()->type)); int ret = SSL_use_PrivateKey(ssl, privatekeyevp.get()); if (ret == 1) { privatekeyevp.release(); @@ -2323,7 +2234,8 @@ static void NativeCrypto_SSL_set_tlsext_host_name(JNIEnv* env, jclass, if (hostnameChars.c_str() == NULL) { return; } - JNI_TRACE("NativeCrypto_SSL_set_tlsext_host_name hostnameChars=%s", hostnameChars.c_str()); + JNI_TRACE("ssl=%p NativeCrypto_SSL_set_tlsext_host_name hostnameChars=%s", + ssl, hostnameChars.c_str()); int ret = SSL_set_tlsext_host_name(ssl, hostnameChars.c_str()); if (ret != 1) { @@ -2421,6 +2333,7 @@ static jint NativeCrypto_SSL_do_handshake(JNIEnv* env, jclass, ret = 0; while (appData->aliveAndKicking) { errno = 0; + if (!appData->setCallbackState(env, shc, fdObject)) { // SocketException thrown by NetFd.isClosed SSL_clear(ssl); @@ -2635,8 +2548,7 @@ static jobjectArray NativeCrypto_SSL_get_peer_cert_chain(JNIEnv* env, jclass, ji */ static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* buf, jint len, int* sslReturnCode, int* sslErrorCode, int timeout) { - - // LOGD("Entering sslRead, caller requests to read %d bytes...", len); + JNI_TRACE("ssl=%p sslRead buf=%p len=%d", ssl, buf, len); if (len == 0) { // Don't bother doing anything in this case. @@ -2653,14 +2565,12 @@ static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* b while (appData->aliveAndKicking) { errno = 0; - // Lock if (MUTEX_LOCK(appData->mutex) == -1) { return -1; } unsigned int bytesMoved = BIO_number_read(bio) + BIO_number_written(bio); - // LOGD("Doing SSL_Read()"); if (!appData->setCallbackState(env, shc, fdObject)) { MUTEX_UNLOCK(appData->mutex); return THROWN_SOCKETEXCEPTION; @@ -2672,7 +2582,13 @@ static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* b sslError = SSL_get_error(ssl, result); freeSslErrorState(); } - // LOGD("Returned from SSL_Read() with result %d, error code %d", result, sslError); + JNI_TRACE("ssl=%p sslRead SSL_read result=%d sslError=%d", ssl, result, sslError); +#ifdef WITH_JNI_TRACE_DATA + for (int i = 0; i < result; i+= WITH_JNI_TRACE_DATA_CHUNK_SIZE) { + int n = std::min(result - i, WITH_JNI_TRACE_DATA_CHUNK_SIZE); + JNI_TRACE("ssl=%p sslRead data: %d:\n%*s", ssl, n, n, buf+i); + } +#endif // If we have been successful in moving data around, check whether it // might make sense to wake up other blocked threads, so they can give @@ -2688,7 +2604,6 @@ static int sslRead(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, char* b appData->waitingThreads++; } - // Unlock MUTEX_UNLOCK(appData->mutex); switch (sslError) { @@ -2882,8 +2797,7 @@ static jint NativeCrypto_SSL_read(JNIEnv* env, jclass, jint ssl_address, jobject */ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const char* buf, jint len, int* sslReturnCode, int* sslErrorCode) { - - // LOGD("Entering sslWrite(), caller requests to write %d bytes...", len); + JNI_TRACE("ssl=%p sslWrite buf=%p len=%d", ssl, buf, len); if (len == 0) { // Don't bother doing anything in this case. @@ -2901,6 +2815,7 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const while (appData->aliveAndKicking && len > 0) { errno = 0; + if (MUTEX_LOCK(appData->mutex) == -1) { return -1; } @@ -2919,7 +2834,13 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const sslError = SSL_get_error(ssl, result); freeSslErrorState(); } - // LOGD("Returned from SSL_write() with result %d, error code %d", result, error); + JNI_TRACE("ssl=%p sslWrite SSL_write result=%d sslError=%d", ssl, result, sslError); +#ifdef WITH_JNI_TRACE_DATA + for (int i = 0; i < result; i+= WITH_JNI_TRACE_DATA_CHUNK_SIZE) { + int n = std::min(result - i, WITH_JNI_TRACE_DATA_CHUNK_SIZE); + JNI_TRACE("ssl=%p sslWrite data: %d:\n%*s", ssl, n, n, buf+i); + } +#endif // If we have been successful in moving data around, check whether it // might make sense to wake up other blocked threads, so they can give @@ -2938,7 +2859,7 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const MUTEX_UNLOCK(appData->mutex); switch (sslError) { - // Successfully write at least one byte. + // Successfully wrote at least one byte. case SSL_ERROR_NONE: { buf += result; len -= result; @@ -2971,7 +2892,7 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const break; } - // An problem occurred during a system call, but this is not + // A problem occurred during a system call, but this is not // necessarily an error. case SSL_ERROR_SYSCALL: { // Connection closed without proper shutdown. Tell caller we @@ -2997,7 +2918,7 @@ static int sslWrite(JNIEnv* env, SSL* ssl, jobject fdObject, jobject shc, const } } } - // LOGD("Successfully wrote %d bytes", count); + JNI_TRACE("ssl=%p sslWrite => count=%d", ssl, count); return count; } @@ -3248,16 +3169,6 @@ static jlong NativeCrypto_SSL_SESSION_get_time(JNIEnv* env, jclass, jint ssl_ses } /** - * Our implementation of what might be considered - * SSL_SESSION_get_version, based on SSL_get_version. - * See get_ssl_version above. - */ -// TODO move to jsse.patch -static const char* SSL_SESSION_get_version(SSL_SESSION* ssl_session) { - return get_ssl_version(ssl_session->ssl_version); -} - -/** * Gets and returns in a string the version of the SSL protocol. If it * returns the string "unknown" it means that no connection is established. */ |